【python】@property, @xx.setter, __set__

@property,可以用来实现类内方法像变量一样被读取。适合一些格式化输出方法,好处是既访问了类内变量,又保护了类内变量不被外部操作。举例如下:

class A:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    @property
    def profile(self):
        return "name: "+ self.name + "\nage: " + str(self.age)

if __name__ == "__main__":
    a = A("Lily", 18)
    print(a.profile)

"""
output:

name: Lily
age: 18
"""

如果没有使用@property,那么要用a.profile()调用。

@xx.setter与@property对应。它需要先定义@property方法,假设方法名为a,则紧跟@a.setter。它的应用场景是对property方法涉及的类内变量进行赋值,进而改变property的返回结果。这种调用方法可以提醒人:更改类内变量的值是为了property方法。调用方式为xx=v。v对应的是setter中传的变量值。

#coding=utf8

class B:
    def __init__(self, p):
        self.price = p


    @property
    def actual_price(self):
        return "真实价格:" + str(self.price)

    @actual_price.setter
    def actual_price(self, d):
        self.price = self.price * d

if __name__ == "__main__":
    b = B(100)
    print(b.actual_price)
    b.actual_price = 0.3
    print(b.actual_price)

'''
output:

真实价格:100
真实价格:30.0
'''

如果在对类变量赋值时需要一些判断,那么,需要用到__set__( )。例子如下:

class age_filter:
    def __init__(self, key_name, threshold, default_value):
        self.key_name = key_name
        self.threshold = threshold
        self.default_value = default_value

    def __set__(self, obj, value):
        if value > self.threshold:
            print("correct set !")
            obj.__dict__[self.key_name] = value # 特别注意!不能用setattr(obj, self.key_name, value)
        else:
            print("wrong set !")
            obj.__dict__[self.key_name] = self.default_value # 特别注意!不能用setattr(obj, self.key_name, self.default_value)


class adult:
    age = age_filter("age", 18, -1)
    def __init__(self, n):
        self.age = n

if __name__ == "__main__":
    a = adult(19)
    print(a.age)  # 19
    b = adult(5)
    print(b.age)  # -1

这里例子实现了一个adult类,对age进行赋值时会调用age_filter类里的__set__。__set__里的逻辑是,如果value大于self.threshold,那么用value对adult类实例中的self.key_name赋值,否则,用self.default_value对adult类实例中的self.key_name赋值。

age_filter类里一定要有的变量就是self.key_name,用来记录原实例里被赋值的变量名。否则将无法得知原实例中是对什么变量赋值而触发的这个__set__。__set__只能拿到原实例,也就是它的第二个参数obj。在__set__里,如果想对原实例obj进行赋值,那么一定不能用setattr(obj, self.key_name, value),因为这样会导致无限循环调用__set__。要用obj.__dict__[self.key_name]=value来避开触发__set__。

这个age一定要设置成类变量,也就是在__init__之前先让age=age_filter。这样可以让self.age有一个类型:age_filter。后面不管是__init__还是其他方法,再对age进行赋值时,都会因为修改了age_filter而触发age_filter里的__set__。否则,无法让age以age_filter的身份被修改。注意,这个类变量名age要和传入age_filter的key_name一致,否则__set__里的修改不生效。

你可能感兴趣的:(python,开发语言)