正常情况下,定义了一个class,创建了一个class的实例后,可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。
1.__slots__
限制class实例能添加的属性
使用__slots__
要注意,__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
除非在子类中也定义__slots__
,这样,子类实例允许定义的属性就是自身的__slots__
加上父类的__slots__
。
2.@property
装饰器
把class的方法变成属性调用
3.多重继承
在设计类的继承关系时,通常,主线都是单一继承下来的,如果需要“混入”额外的功能,通过多重继承就可以实现。
例如,Ostrich继承自Bird。让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计通常称之为MixIn。
为了更好地看出继承关系,我们把Runnable和Flyable改为Bird和FlyableMixIn
class Ostrich(Bird, RunnableMixIn, FlyableMixIn):
pass
MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。
4.定制类
-
__str__
返回用户看到的字符串
-
__iter__
如果一个类想被用于for ... in
循环,类似list或tuple那样,就必须实现一个__iter__()
方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()
方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
-
__getitem__
上述案例,像list,但是不能像list一样取出元素
要表现得像list那样按照下标取出元素,需要实现__getitem__()
方法
class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
list的切片方法在这个上面是否能使用呢?
对于Fib却报错。原因是getitem()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断
class Fib(object):
def __getitem__(self, n):
if isinstance(n, int): # n是索引
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L
与之对应的是__setitem__()
方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()
方法,用于删除某个元素。
总之,通过上面的方法,我们自己定义的类表现得和Python自带的list、tuple、dict没什么区别,这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。
-
__call__
类,直接对实例进行调用
判断一个对象是否能被调用,能被调用的对象就是一个Callable
对象。
通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。
5.枚举类
Python提供了Enum类来枚举,定义一个class枚举类型,然后,每个常量都是class的一个唯一实例。
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
这样我们就获得了Mouth类型的枚举类,可以直接使用Mouth.Jan来引用一个常量,或者枚举它的所有成员.
value属性则是自动赋给成员的int常量,默认从1开始计数。