类与对象的用法

一、类的基本使用

  • 类中包括:静态字段(静态变量)、动态字段(动态变量)和构造方法
  • 静态字段写在__init__方法之前,动态字段需要实例化对象类传递参数值,字段名写在__init__方法中
  • 实例化对象的过程:
  • 创建一个对象空间,实例空间
  • 自动执行__init__方法,并将实例化的对象空间传递给self
  • 执行具体的__init__代码,给对象空间封装属性
  • 类名.静态字段名可以直接调用和修改静态字段的信息,在init和其他的构造方法中也可直接的调用,同样的类的实例化对象可以在实例化后实例化对象.静态字段的方式来修改和调用类中定义好的静态字段
  • 实例化对象具备类定义的所有的属性和方法
class Person(object):
	gender = '男'
	hobby = '篮球'
	count = 1
	# 静态字段或者静态变量
	def __init__(self, name, age, level='one'):
		self.name = name
		self.age = age
		self.level = level
		# 动态变量或者动态字段
		Person.hobby = '篮球、乒乓球'
		# 注意:在类中的方法内是可以调用和修改静态字段的,但是调用和修改的时候一定要注意:必须使用类名.字段名的方式来调用
		# 如果不加上类名,程序会报错。
		Person.count = self.count + 1
		# 调用count的时候必须加上self
	def get_name(self):
		print(Person.gender)
		return self.name
		# 构造方法
	def set_name(self, name):
		self.name = name
		return self.name
	def get_age(self):
		return self.age
	def __str__(self):
		return self.level
		# str不许要传递其他的参数,就一个参数self
if __name__ == '__main__':
	per1 = Person('Alex', 18)
	'''
		实例化的过程:
		1.创建一个对象空间,实例空间
		2.自动执行__init__方法,并将实例化的对象空间传递给self
		3.执行具体的__init__代码,给对象空间封装属性
	'''
	print(per1.get_name())
	print(per1.__str__())
	per1.set_name('JackMa')
	print(per1.get_name())
	print(per1.get_age())
	print(Person.__dict__)
	# 类名.__dict__可以直接获得类中方法的方法名和所在内存地址组成的字典
	print(per1.__dict__)
	# 查询对象中的所有的属性和方法
	print(per1.name)
	print(per1.gender)
	# 对象可以直接类中查询__init__封装的属性,也可以直接调用类中的静态字段
	
	print(Person.gender)
	# 类不能直接去查询__init__中属性,对象可以调用和查询,但是类可以直接使用类名调用类中的静态字段


	print(per1.name, per1.gender, per1.age, per1.hobby, per1.level)
	per1.gender='女'
	print(per1.__dict__)
	# 类中所有的方法和属性,实例化对象同样也具有,包括修改和调用静态字段(都是对象本身的,不是类中的定义的)
	print(per1.count)
	per1.count = 4
	print(per1.count)
  • 类的组合使用:组合就是在一个类中把另外一个类的实例化对象封装为当前类的属性,再通过当前类的属性去调用另外一个类中的方法
'''
	使用组合的思维来计算圆环的面积和周长,圆环的面积为:大圆面积减去小圆的面积,圆环的周长为:大院的周长加上小圆的周长
'''
class Circle(object):
	pi = 3.1415926
	def __init__(self, r):
		self.r = r

	def get_area(self):
		area = Circle.pi * self.r * self.r
		# 静态变量的调用必须用类名.静态变量的方式来调用
		return area

	def get_circumference(self):
		circumference = 2 * Circle.pi * self.r
		return circumference
# 通过圆环的计算可以看出最基本的还是圆的周长和面积,所以需要把圆的计算内容抽象成为一个单独的类
# 再创建圆环类来调用基类--圆类
  
class Ring(object):
	def __init__(self, r1, r2):
		self.r1 = Circle(r1)
		self.r2 = Circle(r2)
    # 把大小圆的半径作为参数传递给基类--圆类,再把结果实例化为self.r1、self.r2

	def area(self):
		area = self.r1.get_area() - self.r2.get_area()
		return area

	def circumference(self):
		circumference = self.r1.get_circumference() + self.r2.get_circumference()
		return circumference

r = Ring(20, 10)
print(r.area())
print(r.circumference())

二、类的基础继承

  • 在类继承的时候,如果希望子类中调用父类中的方法,那么在子类中就不能定义与父类中同名的方法,否则会出现方法覆盖。
class Animal(object):
	def __init__(self, name, gender, age):
		self.name = name
		self.gender = gender
		self.age = age

	def eat(self):
		return '%s吃东西' % self.name

	def drink(self):
		return '%s喝东西' % self.name

class Cat(Animal):
	def eat(self):
		print('覆盖父类')
	# 如果希望只执行父类中的方法,那么在子类的继承中就不要写与父类中同名的方法

cat = Cat('tom', 'male', 3)
cat.eat()
  • 类继承中同时调用子类和父类中构造的方法

Note

继承方式一:必须使用类名调用__init__方法, 并且把父类中的参数按照顺序写在子类__init__方法中

继承方式二:使用super(),调用__init__方法,方法中不能写self

一般情况下使用的是super()

class Animal(object):
	def __init__(self, name, gender, age):
		self.name = name
		self.gender = gender
		self.age = age

	def eat(self):
		return '%s吃东西' % self.name

	def drink(self):
		return '%s喝东西' % self.name

class Cat(Animal):
	def eat(self):
		print('覆盖父类')
	# 如果希望只执行父类中的方法,那么在子类的继承中就不要写与父类中同名的方法

cat = Cat('tom', 'male', 3)
cat.eat()

class Dog(Animal):
	def __init__(self, name, gender, age, tail_color):
		# Animal.__init__(self, name, gender, age)
		# 继承方式一:通过类名直接调用__init__()方法,在子类中必须填件父类中的所有的参数名
    super().__init__(name, gender, age)
		# super()方法使用的时候调用的init方法不能写self
		self.tail_color = tail_color

	def shout(self):
		print("狗叫个不停")

dog = Dog('blocktom', 'female', 4, 'gold_yellow')
dog.shout()
print(dog.eat())
print(dog.drink())
  • 继承父类中的方法,如果希望父类和子类中同名的方法不覆盖并且同时调用,那么需要在子类的方法中用super()调用同名的方法接收结果,父类中的方法都参数也需要在子类的同名方法中传递参数,用法和__init__方法用法一致。需要注意的是:super()直接相当于继承的父类,类中的方法可以直接在方法中传递参数,不必须通过__init__参数来传递
class Animal(object):
	def __init__(self, name, gender, age):
		self.name = name
		self.gender = gender
		self.age = age

	def eat(self, arg1):
		return '%s吃%s' % (self.name, arg1)

	def drink(self):
		return '%s喝东西' % self.name

class Cat(Animal):
	def eat(self):
		print('覆盖父类')
	# 如果希望只执行父类中的方法,那么在子类的继承中就不要写与父类中同名的方法

cat = Cat('tom', 'male', 3)
cat.eat()

class Dog(Animal):
	def __init__(self, name, gender, age, tail_color):
		#Animal.__init__(self, name, gender, age)
		# 继承方式一:通过类名直接调用__init__()方法,在子类中必须填件父类中的所有的参数名
		super().__init__(name, gender, age)
		# super()方法使用的时候调用的init方法不能写self
		self.tail_color = tail_color

	def eat(self, arg1):
		result = super().eat(arg1)
		result = result + '  ' + '不覆盖父类中的同名方法'
		return result

	def shout(self):
		print("狗叫个不停")

dog = Dog('blocktom', 'female', 4, 'gold_yellow')
dog.shout()
print(dog.drink())
print()
print(dog.eat('排骨'))

'''
结果是:
覆盖父类
狗叫个不停
blocktom喝东西

blocktom吃排骨  不覆盖父类中的同名方法
'''

三、类的继承的进阶

  • 继承分类:单继承和多继承
  • 类的分类:新式类和经典类
  • 新式类:直接继承object的类
  • 在python3版本中所有的类都是新式类,因为在python3中都默认的继承object类
  • 经典类:不继承object类,python2版本中所有的类默认都不继承object类,所有的类默认都是经典类,可以继承object类,python2既有经典类,也有新式类
  • 单继承的使用:单继承时会先从当前子类查找,如果查找不到对应的方法,会从继承的父类查找,如果父类找不到,会继续往上找,直到object,如果都查找不到, 那么程序就会报错
class A:
	def test(self):
		return 'IN A'

class B(A):
	pass
	# def test(self):
	# 	return 'IN B'

class C(B):
	pass
	# def test(self):
	# 	return 'IN C'

c = C()
print(c.test())

''' 结果是:IN A '''
  • 多继承:
  • 新式类:遵循广度优先
  • 广度优先:即在继承的多个类中首先遵循的是从左至右的原则,先从左边开始查找方法,如果前边的继承类每一个节点都不存在查找的方法, 直到最后一个节点,那么程序就会对比右边的几个继承类中是否存在左边继承类中的最后一个节点,如果存在,那么左边的就不查找最后一个节点,如果右边的继承类中都不存在最后一个节点,那么左边的继承类则会查找最后一个节点,直至查找到对应的方法。
'''广度优先'''
class A:
	def test(self):
		return 'IN A'

class B(A):
	def test(self):
		return 'IN B'

class C(B):
	pass
	# def test(self):
	# 	return 'IN C'

class D(B):
	pass
	# def test(self):
	# 	return 'IN D'

class E(C):
	pass
	# def test(self):
	# 	return 'IN E'

class F(D):
	pass
	# def test(self):
	# 	return 'IN F'

class G(E, F):
	pass
	# def test(self):
	# 	return '广度优先'


g = G()
print(g.test())

'''
在上边的例子中遵循的就是广度优先的原则,E类向上查找,找到C后不再继续查找,因为C继承自B类,而F类继承的节点中也包括B类,此时程序就会在C类出停止查找,开始从F类查找,直至查找到B类,包括查找B类
'''
  • 经典类:遵循深度优先。深度优先会一条路走到底,每一个继承类的所有的节点都会查找完,才会继续后边继承类节点的查找。
  • .mro():通过类名.mro()可以把多继承中查找的路径全部打印出来
print(G.mro())
# 结果是:
[<class '__main__.G'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
  • 继承的优点:
  • 减少代码的冗余
  • 规范代码
  • 提高代码的复用性
  • 提高代码的可维护性
  • 让类与类发生关系