动态绑定属性

绑定属性

正常情况下,当我们定义了一个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