动态绑定属性
绑定属性
正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。先定义class:
class Student(object):
pass
可以看到这个类是没有自己写一些属性。
stu1 = Student();
stu1.name = 'Tom' #动态绑定一个属性
#查看这个属性
print(stu1.name, stu1.__getattribute__('name'))
#打印结果
Tom Tom
不仅仅可以绑定属性,也可以绑定方法!
绑定方法
#定义一个普通函数
def set_age(self, age):
self.age = age
#可以调用这个函数 设置属性
set_age(stu1, 20)
print(stu1.age, stu1.__getattribute__('age'))
from types import MethodType
#但是这个属性不是实例自己的方法 现在给它设置这个方法
stu1.set_age = MethodType(set_age, stu1)
stu1.set_age(33)
print(stu1.age, stu1.__getattribute__('age'))
#打印结果
20 20
33 33
但是要注意的是这个方法只属于stu1的,对于其他实例不起作用:
stu2 = Student()
stu2.set_age(32)
#打印结果
AttributeError: 'Student' object has no attribute 'set_age'
为了给所有实例都绑定方法,可以选择给class绑定方法
类绑定方法
#先打印测试一下有没有这个属性
print(stu1.__getattribute__('set_name'))
#注意此时是会报错的
#AttributeError: 'Student' object has no attribute 'set_name'
#定义一个函数
def set_name(self, name):
self.name = name
#给类绑定这个属性
Student.set_name = set_name
#现在再次测试
print(stu1.__getattribute__('set_name'))
stu1.set_name('Rose')
stu2.set_name('Jack')
print(stu1.name)
print(stu2.name)
#打印结果
#去除报错语句后
<bound method set_name of <__main__.Student object at 0x00000000028FEEB8>>
Rose
Jack
__slots__
我们通过以上实现了动态添加属性,但是假如想要限制实例属性怎么办呢?
比如,只允许对Student实例添加name和age属性。
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:
class Student(object):
__slots__ = ('name', 'age')
stu1 = Student()
stu1.name = 'John'
stu1.age = 21
print(stu1.name, stu1.age)
#打印结果
John 21
但是:
stu1.addr = 'Beijing'
#打印结果
AttributeError: 'Student' object has no attribute 'addr'
使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:
#中学生..
class MidStudent(Student):
pass
stu2 = MidStudent()
stu2.addr = 'Beijing'
print(stu2.addr)
#打印结果
Beijing
除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。
__slots__需要注意的两点
slots只能限制添加属性,不能限制通过添加方法来添加属性
from types import MethodType
class School(object):
__slots__ = ('name')
pass
def set_city(self, city):
self.city = city
School.set_city = MethodType(set_city, School)
sch = School()
sch.name = 'BeiJing University'
sch.set_city('BeiJing')
print(sch.name)
print(sch.city)
#打印结果
BeiJing University
BeiJing
上段代码中,Student类限制属性name,但可以通过添加方法添加一个city属性。
属性分实例属性和类属性,多个实例同时更改类属性,值是最后更改的一个
class School(object):
pass
def set_city(self, city):
self.city = city
School.set_city = MethodType(set_city, School)
sch1 = School()
sch2 = School()
sch1.set_city('BeiJing')
sch2.set_city('ShangHai')
print(sch1.city, sch2.city)
#打印结果
ShangHai ShangHai