正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。
from types import MethodType
class Person(object):
pass
per1 = Person()
per2 = Person()
# 动态绑定属性
per1.name = 'lucy'
per2.name = 'tom'
def showName(self):
print(self.name)
# 动态绑定方法
per1.showName = MethodType(showName,per1)
# 可以调用
per1.showName()
# 无法调用,报错
per2.showName()
可以通过MethodType()
在运行期间动态的给一个实例per1绑定方法,但是,Person类的的其他实例,例如per2无法使用这个方法
可以通过给Person这个类绑定,来实现所有实例使用showName这个方法
class Person(object):
pass
per1 = Person()
per2 = Person()
per1.name = 'lucy'
per2.name = 'tom'
def showName(self):
print(self.name)
# 对Person类动态绑定方法
Person.showName = showName
per1.showName()
per2.showName()
如果我们需要限制实例的属性和方法的话,那么可以使用__slots__
属性,这个属性的值是一个元组Tuple
使用__slots__
要注意,__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
除非在子类中也定义__slots__
,这样,子类实例允许定义的属性就是自身的__slots__
加上父类的__slots__
from types import MethodType
class Person(object):
#声明Person类的实例,只可以有name、age两个属性
__slots__ = ('name','age')
对于类的方法,装饰器一样起作用,Python内置的@property
装饰器就是负责把一个方法变成属性调用的,省去了编写getter、setter的麻烦
注意:此处的方法名称,不要和属性名一样,以免造成堆栈溢出报错
from msilib.schema import Property
class Person(object):
#此处实际的属性应该是__name,而对外暴露的是name,必须通过name来操作属性
@property
def name(self):
return self.__name
#__name属性的setter方法
@name.setter
def name(self,name):
if(len(name) < 3):
print('名字长度必须大于3位')
else:
self.__name = name
per = Person()
per.name = 'lucy'
print(per.name)
由于我们直接打印对象,输出的是<__main__.Student object at 0x109afb190>
这样的字符串,所以,可以重写__str__()
,来进行定制,类似java的toString方法
由于__str__()
方法是返回给用户的字符串,但是如果在调试的时候,控制台上打印的字符串是__repr__()
返回的,所以,在重写完__str__()
后,使用__repr__ = __str__
即可
class Person(object):
def __init__(self,name,age):
self.__name = name
self.__age = age
@property
def name(self):
return self.__name
@name.setter
def name(self,name):
self.__name = name
@property
def age(self):
return self.__age
@age.setter
def age(self,age):
self.__age = age
def __str__(self):
return '姓名:%s,年龄:%s' % (self.__name,self.__age)
__repr__ = __str__
per = Person('lucy','age')
print(per)
如果一个类想被用于for ... in
循环,类似list或tuple那样,就必须实现一个__iter__()
方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()
方法拿到循环的下一个值,直到遇到StopIteration
错误时退出循环。
例如,1-10000之内的斐波那契数列(除第一第二项,其余每项都等于前两项之和)
class Feb(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 > 100000: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
feb = Feb()
for i in feb:
print(i)
正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错,此时除了添加这个属性外,python还提供了一个机制,就是写一个__getattr__()
方法,动态返回一个属性,也可以用来返回方法
class Person(object):
def __init__(self,name):
self.name = name
def __getattr__(self,attr):
if attr == 'age':
return 18
if attr == 'sayHello':
return lambda :print('Hello')
per = Person('lucy')
print(per.age)
per.sayHello()
注意,只有在没有找到属性的情况下,才调用__getattr__
,已有的属性,比如name
,不会在__getattr__
中查找,此外,注意到任意调用如per.abc
都会返回None
,这是因为我们定义的__getattr__
默认返回就是None
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
__reqr__ = __str__
ch = Chain().status.users.list
print(ch)
# /status/users/list
任何类,只需要定义一个__call__()
方法,就可以直接对实例进行调用
class Person(object):
def __init__(self,name,age):
self.name = name
self.age = age
def __call__(self):
print('姓名:%s,年龄:%s' % (self.name,self.age))
per = Person('lucy',18)
per()
Python提供了Enum
类来实现这个功能,定义一个class类型,然后,每个常量都是class的一个唯一实例
value
属性则是自动赋给成员的int
常量,默认从1
开始计数
#导入Enum
from enum import Enum
#定义枚举类Month,并声明枚举项
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装饰器用来帮助检查有没有重复值
@unique
class Weekday(Enum):
Sun = 0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
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
'''
from enum import Enum,unique
# unique装饰器用来帮助检查有没有重复值
@unique
class Weekday(Enum):
Sun = 0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
day = Weekday.Sun
print(day) # Weekday.Sun
print(Weekday.Mon) # Weekday.Mon
print(Weekday['Tue']) # Weekday.Tue
print(day.value) # 0
print(day == Weekday.Sun) # True
print(Weekday(1)) # Weekday.Mon
要定义一个Hello
的class,就写一个hello.py
模块
class Hello(object):
def sayHello(self,name = 'world'):
print('hello %s' % name)
main.py
from hello import Hello
he = Hello()
he.sayHello()
print(type(he)) # <class 'hello.Hello'>
print(type(Hello)) # <class 'type'>
type()
函数可以查看一个类型或变量的类型,Hello
是一个class,它的类型就是type
,而he
是一个实例,它的类型就是class Hello
python中class的定义是运行时动态创建的,而创建class的方法就是使用type()
函数
type()
函数既可以返回一个对象的类型,又可以创建出新的类型,可以通过type直接创建一个Hello
类型,而不需要通过定义,语法:类名 = type('类名',继承的父类元组,dict(类方法=函数)
def func(self,name = 'world'):
print('hello %s' % name)
Hello = type('Hello',(object,),dict(sayHello = func))
he = Hello()
he.sayHello()
通过type()
函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()
函数创建出class