python - 面向对象
类和对象
类(Class):字段和方法是类的属性。(这两个术语,用以区分普通的变量和函数)只有当你拥有一个该类的对象时,你才能使用这些字段和方法。
对象(Object):是类的实例
1 class Person: # 类:关键字class 类名 冒号 2 pass 3 4 5 p = Person() # 对象:类名后跟一对括号 6 print(p) # 打印 <__main__.Person object at 0x00532E08>
self:
类方法与普通函数只一个区别,类方法必须有一个额外的参数self,位于参数列表开头,但是不用在调用这个方法时为这个参数赋值,python会自动为它提供。这种特定的变量引用的是对象本身。如果你有一个没有参数的方法,也至少有一个参数self。
1 class Person: 2 def say_hi(self): # say_hi()方法不需要参数,但函数定义中仍有self 3 print('hi') 4 5 6 p = Person() 7 p.say_hi()
__init__()方法
1 class Person: 2 def __init__(self, name): # 构造函数,在类的对象初始化时自动调用 3 self.name = name 4 5 def say_hi(self): 6 print('hi,my name is {}'.format(self.name)) 7 8 9 p = Person('zhangsan') # 隐式调用__init__()方法 10 p.say_hi()
实例变量和类变量
类的字段有两种类型:类变量与对象变量。它们根据是类还是对象拥有这些变量来分类。
- 类变量:是共享的,它们可以被属于该类的所有实例访问。该类变量只有一个副本,当任何一个对象对类变量做出改变时,发生的变动将在其它所有实例中得到体现。
- 对象变量:由类的每一个独立的对象或实例所拥有。每个对象都拥有属于自己的字段副本,也就是说,它们不会被共享,也不会以任何方式与其它不同实例中的相同名称的字段产生关联。
- 类方法:属于类的方法,也叫静态方法。该方法中引用类变量。可以使用装饰器将一个方法标记为类方法
引用类变量:类名.变量名 引用对象变量:self.变量名 引用类方法:类名.方法名 class Robot: num = 0 # number属于Robot类,因此它是一个类变量 def __init__(self, name): self.name = name # name属于一个对象,因此它是一个对象变量 Robot.num += 1 # 引用类变量(类名.变量名)也可以使用:self.__class__.number += 1 因为每个对象都通过self.__class__属性来引用它的类 def sayhi(self): print(f'hi, {self.name}') # 引用对象变量(self.变量名) @classmethod # 使用装饰器decorator将print_num()方法标记为类方法 def print_num(cls): print(f'num={Robot.num}') # 机器人1 r1 = Robot('zhangsan') r1.sayhi() Robot.print_num() r1.print_num() # 机器人2 r2 = Robot('lisi') r2.sayhi() Robot.print_num() r2.print_num() print(id(Robot.num), Robot.num) # 此处r1.num,r2.num,Robot.num指向同一个内存地址,是同一个变量 print(id(r1.num), r1.num) print(id(r2.num), r2.num)
打印如下:
hi, zhangsan
num=1
num=1
hi, lisi
num=2
num=2
4389296960 2
4389296960 2
4389296960 2
以下定义了对象变量r1.num后,r1.num和Robot.num就只是同名,但实质不同的两个变量
r1.num += 3 print(id(Robot.num), Robot.num) # 此处r2.num,Robot.num指向同一个内存地址,r1.num指向另一个不同的地址 print(id(r1.num), r1.num) print(id(r2.num), r2.num)
打印如下:
4330748736 2
4330748832 5
4330748736 2
私有变量:
所有类成员都是公开的,除非使用双下划线作为前缀,例如:__privatevar,python会认为是一个私有变量
继承:
1. 父类,子类
1 class SchoolMember: 2 def __init__(self, name, age): 3 self.name = name 4 self.age = age 5 print('initialized SchoolMember: {}'.format(self.name)) 6 7 def tell(self): 8 print('name: {}, age: {}'.format(self.name, self.age), end=' ') 9 10 11 class Teacher(SchoolMember): # 继承SchoolMember 12 def __init__(self, name, age, salary): 13 SchoolMember.__init__(self, name, age) # 通过self显式调用父类的__init__方法(如果子类没有定义__init__方法,python会自动调用基类的构造函数) 14 self.salary = salary 15 print('initialized Teacher: {}'.format(name)) 16 17 def tell(self): 18 SchoolMember.tell(self) # 在方法名前面加上类名,再传入self和其他变量,来调用基类方法 19 print('salary:{}'.format(self.salary)) 20 21 22 class Student(SchoolMember): 23 def __init__(self, name, age, marks): 24 SchoolMember.__init__(self, name, age) 25 self.marks = marks 26 print('initialized Student: {}'.format(self.name)) 27 28 def tell(self): 29 SchoolMember.tell(self) 30 print('marks: {}'.format(self.marks)) 31 32 33 t = Teacher('wanglaoshi', 80, 20000) 34 s = Student('lisi', 22, 98) 35 36 mm = [t, s] 37 38 for member in mm: 39 member.tell() # 当使用SchoolMember类的tell()方法时,可以将Teacher或Student的实例看做SchoolMember的实例。python会从当前的实际类型中寻找方法,若子类中未定义tell()方法,则会调用父类的tell()方法
2. 多态性:在不考虑实例类型的情况下使用实例,也就是说,不同类型的实例有相同的调用方法。比如上例中的Teacher、Student、SchoolMember都有tell()方法,t和s可以以相同的形式调用tell()方法。优点:增加程序的可扩展性,例如,SchoolMember增加新的子类,使用者无需更改自己的代码,还是用tell()方法去调用
3. 多重继承:继承元组中有超过一个类,例如:
1 class B: 2 pass 3 4 5 class C: 6 pass 7 8 9 class A(B, C): # 多重继承(继承B和C) 10 pass