今天学习python类看到 @property 的用法觉得很新奇,就自己尝试了很久,刚开始不明白,后来终于明白了点
其实总结一句话就是, @property 把类中的方法调用方式改变成当成属性属性调用方式了,调用方式改变了,作用没变,说的总归没实践明白,来看栗子
class Student: def __init__(self, name, score): self.name = name self.score = score s = Student("jacks", 99) print(s.name) print(s.score)
上面是一个很普通的类,并且实例化了
执行结果也很明显就是
jacks
99
然后我们很容易更改这个score分数,比如这样
s.score = 101
print(s.score)
101
你觉得这样合理吗,虽然python很自由,但是有些变量总归不想让人随便更改,那么怎么办呢?我们可以使用私有变量,然后就变化为如下代码
class Student: def __init__(self, name, score): self.name = name self.__score = score def get_score(self): return self.__score def set_score(self, score): try: if 0 < score <100: self.__score = score else: print("Out of range") except TypeError: print("Only int value")
以上代码不仅隐藏了score变量,更给这个变量赋值增加了判断,超出100或者小于0或者字符串都是不合法的。
并且实例私有变量私有化后,是不能直接赋值,同时也不能直接被拿到,我们来测试下
s = Student("bob", 99) #实例化一个类 print(s.name) print(s.__score) #打印实例name属性和__score print(s.name) print(s.__score) #来看结果 bob Traceback (most recent call last): File "E:/python_learn/learn1.py", line 19, in <module> print(s.__score) AttributeError: 'Student' object has no attribute '__score' #报错了,找不到__score
为什么直接打印不了私有变量呢,因为被隐藏了,需要用上面的get_score方法来调用,我们来测试下
s = Student("bob", 99) print(s.name) #调用实例的get_score方法 print(s.get_score()) #来看下结果 bob 99 #我们得到了正确结果
那么我们怎么来修改这个私有变量呢?上面已经测试过了,直接调用私有变量都不行,那么直接修改肯定也是不可以的
那么我们就需要写一个set_score方法来修改这个私有变量了,然后用实例调用这个方法来修改私有变量
#调用实例的set_score方法 s.set_score(88) print(s.get_score()) #来看结果 88 #已经成功修改了score的值
上面的每次修score值和调用score的值是不是很麻烦?每次都要调用实例方法?有没有一种简便的方法呢?答案是有的,那就是 使用 @property,把实例调用方法的方式变成调用属性一样简单,上面铺垫了那么多就是为了下面的一刻,先来看代码
class Student: def __init__(self, name, score): self.name = name self.__score = score @property def get_score(self): return self.__score @get_score.setter def set_score(self, score): try: if 0 < score <100: self.__score = score else: print("Out of range") except TypeError: print("Only int value") s = Student("bob", 99) #实例化一个类 print(s.get_score) #打印私有变量__score有没有感觉调用方式的不同?有就对了,以前调用方式这样的 print(s.get_score()) 是调用方法的方式,现在是用属性调用的方式 s.set_score = 89 #同上,以前设置私有__score是用 s.set_score(89)这样的调用方法方式,现在也是用属性赋值的方式 print(s.get_score) #打印私有__score的值,同上,调用方法也不同了 89 #值改变了
所以这就是 @property 的神奇之处,把一个实例的方法的使用方式变成属性的使用方式,简便了很多
至于 @get_score.setter 在上面使用 @property 就会产生这个setter的子类
@property 等价于 上面的get_score方法
@get_score.setter 等价于上面的set_score方法
同样的,上面代码中,无论是方法调用方式还是属性调用方式,定义在类中的方法名或者叫做后来的属性名在你的实例调用时候一定要与代码中相同,即红色的部分
下面代码和上面相同的只是方法名变了,实例调用名也变了
class Student: def __init__(self, name, score): self.name = name self.__score = score @property def score(self): return self.__score @score.setter def scores(self, score): try: if 0 < score <100: self.__score = score else: print("Out of range") except TypeError: print("Only int value") s = Student("bob", 99) print(s.score) s.scores = 89 print(s.score)
上面的对于私有属性保护很有用,对于公开的没啥用,公开属性你可以随便就赋值定义了,还保护个屁啊
下面拓展一些东西
先看代码
class Student: def __init__(self, name, score): self.__name = name self.__score = score def get_value(self): return self.__name, self.__score s = Student("jacks", 99) print(s.get_value()) #看下结果 ('jacks', 99)
然后我们给实例增加一个属性并赋值
s.__name = "TOM" #你觉得这个__name和实例里的__name相同吗?
我们来打印下
print(s.__name) #看结果 TOM #居然可以直接调用并打印,看来这个并不是私有属性啊,为什么会这样呢?不应该和上面是一样的吗? 如果你理解为一样的话,那就错了,如果你理解不一样那就对了 #我们来看下实例所有属性 print(s.__dict__) #看结果 {'_Student__name': 'jacks', '_Student__score': 99, '__name': 'TOM'}
实例的私有属性name是 '_Student__name'
而新增的则是 '__name' 两个截然不同的属性
所以大家不要被误解了。