class 个本地函数中的访问描述符

access descriptor in class local functions

我想知道如何在不同的函数中访问描述符?我可以在初始化 Vehicle class 时将速度初始化为描述符,但是我无法在 Vehicle 函数中访问它的值 (calc_speed()-> self.speed.value) returns AttributeError: 'int' object has no attribute 'value'。另外,改变值 audi.speed = 120 不应该触发 set 函数(以及打印应该触发 get 函数)?

class SpeedDesc(object):

    def __init__(self, name, val):
        self.var_name = name
        self.value = val

    def __get__(self, obj, objtype):
        print('Getting', self.var_name)
        return self.value

    def __set__(self, obj, value):
        msg = 'Setting {name} to {value}'
        print(msg.format(name=self.var_name, value=value))
        self.value = value

class Vehicle(object):

    def __init__(self, vType):
        self.vehicle_type = vType
        self.speed = SpeedDesc('speed desc', 100)

    def calc_speed(self, accel):
        return self.speed.value * accel

if __name__ == '__main__':

    audi = Vehicle('sedan')
    print('vehicle speed:', audi.speed.value)
    audi.speed = 120
    print(audi.calc_speed(1.5))
vehicle speed: 100
Traceback (most recent call last):
  File "descriptor_example.py", line 31, in <module>
    audi.calc_speed(1.5)
  File "descriptor_example.py", line 24, in calc_speed
    return self.speed.value * accel
AttributeError: 'int' object has no attribute 'value'

我期待的

Getting speed desc
vehicle speed: 100
Setting speed desc to 120
180

speed 应该是 class 属性;当您通过实例而不是 class 访问它时,__get__ 会传递您想要其速度的实例。我稍微调整了 SpeedDesc 的定义以强调您仍然可以从 Vehicle.__init__.

初始化它

您永远不需要显式访问描述符的 value 属性:这是 __get____set__ 的实现细节。事实上,因为 Vehicle 的所有实例共享 oneSpeedDesc 实例,所以您不想将速度存储在 self.value.您应该将它存储在一个将速度与特定实例相关联的字典中,这最容易通过将其附加到作为 obj 参数接收的对象来完成。

class SpeedDesc(object):

    def __init__(self, name, val=None):
        self.var_name = name
        self.default = val
        self.attr_name = "_" + name  # e.g.

    def __get__(self, obj, objtype):
        if obj is None:
            return self
        print('Getting', self.var_name)
        return getattr(obj, self.attr_name, self.default)

    def __set__(self, obj, value):
        msg = 'Setting {name} to {value}'
        print(msg.format(name=self.var_name, value=value))
        setattr(obj, self.attr_name, value)


class Vehicle(object):
    speed = SpeedDesc('speed desc')

    def __init__(self, vType):
        self.vehicle_type = vType
        self.speed = 100

    def calc_speed(self, accel):
        return self.speed * accel


if __name__ == '__main__':

    audi = Vehicle('sedan')
    # Produces a call to Vehicle.speed.__get__(audi, Vehicle)
    print('vehicle speed:', audi.speed)
    # Produces a call to Vehicle.speed.__set__(audi, 120)
    audi.speed = 120
    print(audi.calc_speed(1.5))

请注意,您不一定需要明确传递名称; __set_name__ 方法可用于获取分配给描述符的属性的名称。

class SpeedDesc(object):

    def __init__(self, val=None):
        self.default = val

    def __get__(self, obj, objtype):
        if obj is None:
            return self
        print('Getting', self.var_name)
        return getattr(obj, self.attr_name, self.default)

    def __set__(self, obj, value):
        msg = 'Setting {name} to {value}'
        print(msg.format(name=self.var_name, value=value))
        setattr(obj, self.attr_name, value)

    def __set_name__(self, owner, name):
        self.var_name = name
        self.attr_name = "_" + name  # e.g.
        print(f"Created {self.var_name} for {owner}, backed by {self.attr_name}")

class Vehicle(object):
    speed = SpeedDesc()

    def __init__(self, vType):
        self.vehicle_type = vType
        self.speed = 100

    def calc_speed(self, accel):
        return self.speed * accel

类型定义完成后,Vehicle.speed.__set_name__(Vehicle, "speed")被代为调用。