slots
python类似js,是一门动态语言,可以动态的给类或者实例添加方法和属性,给类添加的方法和属性可以应用多所有该类的实例。
class Class:
pass
Class.name='i am class'
obj=Class()
print(obj.name) #i am class
obj.myName='i am obj'
print(obj.myName) #i am obj
def printName(self):
print(self.name)
Class.printName=printName
#即使在类动态添加方法之前实例化,还是可以调用之后动态添加的方法
obj.printName() #i am class
如果想限制实例可以添加的属性,可以在定义类的时候使用__slots__
。
class Class:
__slots__=('name','age')
obj=Class();
obj.name='lisong'
obj.age=18
print(obj.name,obj.name) #lisong 18
obj.title='title' #报错
#__slots__只能限制实例的动态添加属性和方法,类依然能够动态添加
Class.title='class title'
print(obj.title) #class title
@property
@property修饰器可以把类的一个方法变成属性,这样就可以拦截取值操作了,同时这个修饰器还会创造一个以方法名称为后缀的setter修饰器,使用这个修改可以拦截取值操作。@property类似于js中的defineProperty。
class Class:
@property
def name(self):
print('return:'+self._name)
return self._name
@name.setter
def name(self,name):
print('set:'+name)
self._name=name
@property
def age(self):
return 18
obj=Class();
obj.name='lisong' #set:lisong
obj.name #return:lisong
obj.age=20 #报错,没有定义setter,只能读取,不能设置
print(obj.age) #18
多重继承
python天生支持多重继承,而静态语言java只支持单继承。
在类的设计中,如果想让某一个类既有A类的功能,又有B类的功能,只需要让这个类同时继承A类和B类就行了,这种设计通常称之为Mixin。
class Runnable:
def run(self):
print('i can run')
class Flyable:
def fly(self):
print('i can fly')
class SuperMan(Runnable,Flyable):
pass
man=SuperMan()
man.run() #i can run
man.fly() #i can fly
定制类
在类中重定义形如__xxx__
的方法,可以让类的实例有特殊的行为。
len
class Class:
def __len__(self):
return 10
len(Class()) #10
str
class Class:
def __str__(self):
return 'i am class'
#默认__str__()打印出内存地址,可以重定义
print(Class()) #i am class
iter
如果一个类想被用于for … in循环,类似list或tuple那样,就必须实现一个__iter__()
方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()
方法拿到循环的下一个值,直到遇到StopIteration
错误时退出循环。
class Class:
def __iter__(self):
return (x*x for x in [1,2,3])
for x in Class():
print(x) #1 2 3
class Class:
sum=0
def __iter__(self):
return self
def __next__(self):
self.sum+=1
if(self.sum>3):
raise StopIteration
else:
return self.sum
for x in Class():
print(x) #1 2 3
getitem
使实例可以作用于下标操作符。
class Class:
arr=[1,2,3,4,5,6]
def __getitem__(self,n):
if(isinstance(n,int)):
return self.arr[n]
if(isinstance(n,slice)):
start=n.start
stop=n.stop
return self.arr[start:stop]
obj=Class()
print(obj[2]) #3
print(obj[1:3]) #2,3
getattr
当属性或方法不存在时会调用该方法。
class Class:
def __getattr__(self,attr):
if(attr=='name'):
return 'lisong'
if(attr=='age'):
return lambda:18
obj=Class()
obj.name #lisong
obj.age() #18
call
如果定义了__call__
方法,实例可以当做方法调用。
class Class:
def __call__(self):
print('run __call__')
obj=Class()
obj() #run __call__
枚举
枚举对象自动赋给成员int常量,默认从1开始计数
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
#Jan => Month.Jan , 1
#Feb => Month.Feb , 2
#Mar => Month.Mar , 3
#Apr => Month.Apr , 4
#May => Month.May , 5
#Jun => Month.Jun , 6
#Jul => Month.Jul , 7
#Aug => Month.Aug , 8
#Sep => Month.Sep , 9
#Oct => Month.Oct , 10
#Nov => Month.Nov , 11
#Dec => Month.Dec , 12
通过继承Enum,可以自定义枚举成员的值
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
print(Weekday.Mon) #1
Weekday.Mon=10 #报错,不可以重新赋值
for name, member in Weekday.__members__.items():
print(name, '=>', member, ',', member.value)
#Sun => Weekday.Sun , 0
#Mon => Weekday.Mon , 1
#Tue => Weekday.Tue , 2
#Wed => Weekday.Wed , 3
#Thu => Weekday.Thu , 4
#Fri => Weekday.Fri , 5
#Sat => Weekday.Sat , 6
动态创建类
type方法
type动态创建类需要三个参数
- class的名称;
- 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
- class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
def hello(self):
print('hello world')
Hello=type('Hello',(object,),{'hello':hello})
Hello().hello() #hello world
metaclass
metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。
当定义一个类的时候,传递一个metaclass关键字参数,则定义了当创建该类的时候需要使用该关键字所指向的类的__new__
方法来创建。
__new__()
方法接收到的参数依次是:
- 当前准备创建的类;
- 类的名字;
- 类继承的父类集合;
- 类的方法、属性集合。
class superMetaclass(type):
def __new__(obj,name,bases,attrs):
print(obj) #<class '__main__.superMetaclass'>
attrs['test'] = lambda self:'this is a test'
return type.__new__(obj,name,bases,attrs)
class Class(metaclass=superMetaclass):
def run(self):
print('running')
obj=Class()
obj.test() #this is a test
obj.run() #running