1.使用__slots__
给特定实例动态绑定方法:
class Student():
pass
s = Student()
def set_age(self,age):
self.age = age
from types import MethodType
s.set_age = MethodType(set_age,s)
s.set_age(99)
print(s.age)
给所有实例动态绑定方法,即给类动态添加方法:
Student.set_age = set_age
s2 = Student()
s2.set_age(88)
s2.age
__slots__的使用场景是给类限定特定的参数名,子类继承则不受限制,除非子类也使用__slots__,那么所有的属性包含子类和父类限定的这些个属性之和。
举例:
class Student():
__slots__ = ('name','age') #用tuple定义允许绑定的属性名称
s = Student()
s.name = 'Macol'
s.age = 23
s.score = 89 #erro
class GradeStudent(Student):
__slots__ = ('score') #用tuple定义允许绑定的属性名称
s1 = GradeStudent()
s1.score = 89
2.@property 是为了一方面可以对内部数据属性进行检查,另一方面又能通过实例直接操作属性而设置的。
class Student(object):
def get_score(self):
return self.__score
def set_score(self,score):
if not isinstance(score,int):
raise ValueError('score must be an int!')
if score>100 or score<0:
raise ValueError('score must be 0<=score<=100!')
else:
self.__score = score
s = Student()
print(s.set_score(int(99)))
print(s.get_score())
#@property的实现比较复杂,我们先考察如何使用。把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,
# 负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作:
class Student1(object):
@property #把一个getter方法变成属性
def score(self):
return self.__score
@score.setter #负责把一个setter方法变成属性赋值
def score(self, score):
if not isinstance(score, int):
raise ValueError('score must be an int!')
if score > 100 or score < 0:
raise ValueError('score must be 0<=score<=100!')
else:
self.__score = score
s1 =Student1()
s1.score = 98
print(s1.score)
3.多重继承
在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich
继承自Bird
。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich
除了继承自Bird
外,再同时继承Runnable
。这种设计通常称之为MixIn。
为了更好地看出继承关系,我们把Runnable
和Flyable
改为RunnableMixIn
和FlyableMixIn
。类似的,你还可以定义出肉食动物CarnivorousMixIn
和植食动物HerbivoresMixIn
,让某个动物同时拥有好几个MixIn:
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
pass
4.定制类
1)、__str__ 类调用显示,__repr__
这是因为直接显示变量调用的不是__str__()
,而是__repr__()
,两者的区别是__str__()
返回用户看到的字符串,而__repr__()
返回程序开发者看到的字符串,也就是说,__repr__()
是为调试服务的。
解决办法是再定义一个__repr__()
。但是通常__str__()
和__repr__()
代码都是一样的,所以,有个偷懒的写法:
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name=%s)' % self.name
__repr__ = __str__
2)、__iter__ 类
如果一个类想被用于for ... in
循环,类似list或tuple那样,就必须实现一个__iter__()
方法,该方法返回一个迭代对象
,然后,Python的for循环就会不断调用该迭代对象的__next__()
方法拿到循环的下一个值,直到遇到StopIteration
错误时退出循环。
#我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
self.a,self.b = self.b,self.a+self.b
if self.a >1000:
raise StopIteration()
return self.a
for n in Fib():
print(n)
3)、
__getitem__ 、__setitem__、__delitem__
Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素:
4
__getattr__)、
要避免这个错误,除了可以加上一个score
属性外,Python还有另一个机制,那就是写一个__getattr__()
方法,动态返回一个属性。修改如下:
class Student(object):
def __init__(self):
self.name = 'Michael'
def __getattr__(self, attr):
if attr=='score':
return 99
#利用完全动态的__getattr__,我们可以写出一个链式调用
class Chain(object):
def __init__(self,path=''):
self._path = path
def __getattr__(self, path):
return Chain('%s/%s' % (self._path,path))
def __str__(self):
return self._path
__repr__ = __str__
print(Chain('home').usr.bin.env)
5)、__call__
任何类,只需要定义一个__call__()
方法,就可以直接对实例进行调用,能不能调用可用callable(Student())进行判断。
class Student(object):
def __init__(self,name):
self.name = name
def __call__(self):
print('My name is %s'%self.name)
s = Student('Bob')
s()
小结
Python的class允许定义许多定制方法,可以让我们非常方便地生成特定的类。
本节介绍的是最常用的几个定制方法,还有很多可定制的方法,请参考Python的官方文档。
5.使用枚举类:
更好的方法是为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了Enum
类来实现这个功能:
from enum import Enum
WEEK = Enum('WEEK',('SUN','MON','TUE','WEN','THU','FRI','SAT'))
print(type(WEEK))
print(WEEK)
for name,value in WEEK.__members__.items():
print(name,value)
from enum import Enum, unique
@unique
class Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
for i in Weekday.__members__.items():
print(i)
6.使用元类:
1)、type(),我们type(Student)一个类时,返回的是type类型,一个实例的时候是class。
type(),即可以返回一个类型,也可以创建一个类型。
先定义一个函数
def fn(self,name):
self.name = name
print('Hello, %s.' % name)
Student = type('Student',(object,),dict(hello=fn))
要创建一个class对象,type()
函数依次传入3个参数:
- class的名称;
- 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
- class的方法名称与函数绑定,这里我们把函数
fn
绑定到方法名hello
上。
通过type()
函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()
函数创建出class。
正常情况下,我们都用class Xxx...
来定义类,但是,type()
函数也允许我们动态创建出类来,也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同,要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,会非常复杂。
2)、metaclass
目前看不懂。