Python中通过class
关键字定义类,object
表示从哪个类继承下来的。
1 | class Student(object): |
访问权限控制
Python中通过把属性的名称钱加上两个下划线__
,这就表明这是一个私有变量,只有内部可以访问。有时候,可能会遇到单下划线开头的实例变量,比如:_name
。这样的实例变量外部是可以访问的,但是当我们看到这样的实例变量的时候,要把它看做实例变量,不要随意访问。注意:变量名类似于 __xxx__ 这种变量是特殊变量,可以直接访问,不是私有变量。我们不能用 __name__ 这样的变量名称
。
1 | class Student(object): |
外部不能访问__name
的原因是:Python解释器将__name
变量改成了_Student__name
,所以仍然可以通过_Student__name
来访问__name
变量,不过不推荐这么做。
1 | # Tom |
继承
1 | class Animal(object): |
多态
由于Dog是从Animal继承而来,所以Dog可以看成为Animal类型,而Animal不能看成Dog。可以用isinstance()
来判断变量是否是某个类型:
1 | isinstance(animal, Animal) |
现在给Animal对象增加一个函数,接受一个Animal
类型的变量:
1 | def run_twice(animal): |
使用多态的好处:当传入Cat或者Dog时,我们只需要接收Animal类型就可以了。因为 Cat、Dog 都是Animal类型,然后按照Animal类型进行操作,由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法。
对于一个变量,只需要知道是Animal类型,无需确切的知道其子类型,就可以放心的调用run()方法,具体是调用Dog还是Cat类型的run()方法,由运行时该对象的确切类型决定。这就是多态的厉害之处,调用方只管调用,不管细节。当我们新增一种Animal的子类时,只要确保run()方法编写正确,不管原来的代码是如何调用的。这就是著名的开闭原则
。
对扩展开放:允许新增Animal子类;
对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。
鸭子类型
鸭子类型
并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的。对于静态语言来说,如果需要传入Animal
类型,则传入的对象必须是Animal
类型或者其子类。对于Python,只要保证传入的对象有一个run()方法就可以了
:
1 | def Timer(object): |
获取对象信息
1 | # 使用 type 判断对象类型 |
实例属性、类属性
1 |
|
类属性虽然归类所有,但是类的实例都可以访问到,实例属性千万不能与类属性同名,相同名称的实例属性会屏蔽掉类属性:
1 | s = Student() |
__slots__
使用__slots__
可以限定实例的属性:
1 | class Student(object): |
注意:
使用__slots__
仅对当前的类实例有效,对其子类是无效的。
__str__
__str__
用来做格式化输出:
1 | class Student(object): |
__iter__
如果一个类想被用于for ... in
循环,类似于list那样,必须实现一个 __iter__()
方法,改方法返回一个迭代对象,然后,for循环就会不断的调用该对象的__iter__()
方法获取的循环的下一个值,直到遇到StopIteration
错误时退出循环。
1 | class Fib(object): |
__getitem__
如果让一个类像数组那样支持下表运算,需要实现__getitem__()
方法:
1 | class Fib(object): |
__getattr__
当调用类的方法或属性时,如果不存在,就会报AttributeError
错误,使用__getattr__()
,动态返回一个属性:1
2
3
4
5
6
7
8
9
10
11class Student(object):
def __init__(self):
self.name = 'Tom'
def __getattr__(self, attr):
if attr=='age':
return 18
s = Student()
# 如果没有定义 __getattr__ 方法,则会报AttributeError错误
s.age
注意:
在没有实现__getattr__
方法时,Student类并没有定义age属性,如果访问age属性,会出现AttributeError: 'Student' object has no attribute 'age'
错误。如果实现了__getattr__
方法,就会动态添加属性。只有没有找到某个属性的情况下,才会调用__getattr__
属性。
这里有一个利用 __getattr__
设计 REST API 的例子:
1 | class Chain(object): |
__call__
在调用实例方法的时候,通常使用instance.method()
这种方式调用。然而,也可以通过实现__call__()
实现实例本身调用。
1 | class Student(object): |
MixIn
Python是支持多继承的,在不支持多继承的编程语言当中,通常需要设计复杂的继承关系。而Python可以通过MixIn
这种方式,为类增加额外的功能。在编写程序的过程中,只要保证主线采取单一继承,通过MixIn
来给一个类增加额外的功能。
Enum
Python中也支持Enum类:1
2
3
4
5
6
7
8from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
# 这样我们就获得了Month类型的枚举类。可以枚举它的所有成员:
# value 属性是自动赋给成员的 int 常量,默认为1开始
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
也可以更精确的控制枚举类型,从Enum派生出自定义类。@unique
装饰器用来保证没有重复值1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23from enum import Enum, unique
class Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
day1 = Weekday.Mon
print(day1)
Weekday.Mon
print(Weekday.Tue)
Weekday.Tue
'Tue']) print(Weekday[
Weekday.Tue
print(Weekday.Tue.value)
2
print(day1 == Weekday.Mon)
True