接着上一篇筆記,咱們來看看內置裝飾器property
、staticmethod
、classmethod
python
class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 man = Celsius(-280) print('dirctly get:', man.temperature) print('to_fahrenheit', man.to_fahrenheit()) # 查看示例的屬性 print(man.__dict__)
dirctly get: -280 to_fahrenheit: -472.0 {'temperature': -280}
這裏直接經過實例來修改類的屬性值,但從輸出能夠看到溫度超過了絕對零度-273.15卻沒有保錯,這是一個bug,下面來修復下。redis
get_xxx
, set_xxx
方法修改類的屬性值class Celsius: def __init__(self, temperature = 0): self.set_temperature(temperature) def to_fahrenheit(self): return (self.get_temperature() * 1.8) + 32 # new update def get_temperature(self): return self._temperature def set_temperature(self, value): if value < -273: raise ValueError("Temperature below -273 is not possible") self._temperature = value # 超過絕對零度報錯 # man = Celsius(-280) man = Celsius(-28) # temperature改成_temperature了 #print('dirctly get:', man.temperature) print('dirctly get:', man._temperature) print('use get_temperature get:', man.get_temperature()) print('to_fahrenheit:', man.to_fahrenheit()) # 查看示例的屬性 print(man.__dict__)
dirctly get: -28 use get_temperature get: -28 to_fahrenheit: -18.4 {'_temperature': -28}
這裏用一個私有變量_temperature
來對temperature
作限制。新增了get_xxx
, set_xxx
方法來修復了bug,但這樣的話用到了這個類的代碼都要修改了,獲取溫度值要將obj.temperature
修改成obj.get_temperature()
,修改溫度值要將obj.temperature = val
修改成obj.set_temperature(val)
。若是有成千上萬行代碼用到這個類的話,顯然這樣修改是不行的,下面使用@property
來解決。mongodb
@property
裝飾器修改類的屬性值class Celsius: def __init__(self, temperature = 0): # 不知道是否是做者寫多了‘_’下劃線,若是用私有變量_temperature的話, # 在類初始化時就賦值並不會調用setter判斷溫度是否大於-273度,因此還會出現bug # self._temperature = temperature self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 @property def temperature(self): print("Getting value") return self._temperature @temperature.setter def temperature(self, value): print('vvvvvv', value) if value < -273: raise ValueError("Temperature below -273 is not possible") print("Setting value") self._temperature = value man = Celsius(-28) print('dirctly get:', man.temperature) # man.temperature(-28)會報錯:TypeError: 'int' object is not callable # 使用property後temperture(self, value)只能經過屬性賦值方式更改 # print('use @temperature.setter:', man.temperature(-28)) man.temperature = -88 print('to_fahrenheit:', man.to_fahrenheit()) print(man.__dict__)
vvvvvv -28 Setting value Getting value dirctly get: -28 vvvvvv -88 Setting value Getting value to_fahrenheit: -126.4 {'_temperature': -88}
@property
裝飾器就是調用了python內置方法property(fget=None, fset=None, fdel=None, doc=None)
,上面@temperature.setter
就至關於fset=temperature
方法。使用了@property
裝飾器,就能夠在不影響原有代碼的狀況下修改類的屬性值。docker
https://www.programiz.com/python-programming/property數據庫
靜態方法的用得不多,當須要一個不訪問類的任何屬性,但它屬於該類的實用函數時就能夠用靜態方法,它只處理傳入的參數。好比下面的靜態方法就單純接收一個參數date
更改下它的鏈接符就返回了,但由於跟類Dates
放一塊兒比較好歸類管理才放到類裏面的。能夠用類直接調用靜態方法,也能夠用類的實例調用靜態方法。api
class Dates: def __init__(self, date): self.date = date def getDate(self): return self.date @staticmethod def toDashDate(date): return date.replace("/", "-") date = Dates("15-12-2016") dateFromDB = "15/12/2016" # 用類直接調用靜態方法 dateWithDash = Dates.toDashDate(dateFromDB) # 用類的實例調用靜態方法 dateWithDash_1 = date.toDashDate(dateFromDB) if(date.getDate() == dateWithDash): print("Equal") if(date.getDate() == dateWithDash_1): print("Equal_1") else: print("Unequal")
Equal Equal_1
https://www.programiz.com/python-programming/methods/built-in/staticmethoddom
from datetime import date # random Person class Person: def __init__(self, name, age): self.name = name self.age = age @classmethod def fromBirthYear(cls, name, birthYear): return cls(name, date.today().year - birthYear) def display(self): print(self.name + "'s age is: " + str(self.age)) person = Person('Adam', 19) person.display() person1 = Person.fromBirthYear('John', 1985) person1.display()
Adam's age is: 19 John's age is: 34
從上面的例子能夠看到,類方法須要有一個必需參數cls
,而不是self
(也能夠是其餘名字,但約定俗成用cls),由於類方法的參數cls
就是這個類自己。,因此cls(name, date.today().year - birthYear)
等同於調用了Person(name, date.today().year - birthYear)
。當咱們調用的類方法return cls(arg1,arg2)
後,由於類方法的參數cls
就是這個類自己,因此就會將這些參數賦值給類的__init__
函數再次初始化。scrapy
有些童鞋應該發現了上面的示例也能夠用靜態方法來實現,但這樣的話它建立的實例就硬編碼爲只屬於基類了(當咱們不但願類的子類更改/重寫方法的特定實現時,靜態方法就派上用場了)。也就是用肯定的一個類Person
代替了cls
,這樣就是硬編碼建立的實例只屬於基類Person
了。若是有類繼承基類的話,使用靜態方法建立的實例只屬於基類,繼承了基類的子類都不屬於實例,而使用類方法的話,繼承了基類的子類建立的實例,既屬於基類也屬於繼承了基類的子類。看下面代碼就懂了。函數
from datetime import date # random Person class Person: def __init__(self, name, age): self.name = name self.age = age @staticmethod def fromBirthYearStaticmethod(name, birthYear): return Person(name, date.today().year - birthYear) @classmethod def fromBirthYearClassmethod(cls, name, birthYear): return cls(name, date.today().year - birthYear) def display(self): print(self.name + "'s age is: " + str(self.age)) class Man(Person): sex = 'Male' class MoreMan(Man): sex = 'Unknown' man = Man.fromBirthYearClassmethod('John', 1985) print('使用類方法') man.display() print('man是類Man的實例嗎', isinstance(man, Man)) print('man是類Person的實例嗎', isinstance(man, Person)) man1 = Man.fromBirthYearStaticmethod('John', 1985) print('使用靜態方法') man1.display() print('man1是類Man的實例嗎', isinstance(man1, Man)) print('man1是類Person的實例嗎', isinstance(man1, Person)) man2 = MoreMan.fromBirthYearClassmethod('John', 1985) print('使用類方法') man2.display() print('man2是類Man的實例嗎', isinstance(man2, Man)) print('man2是類MoreMan的實例嗎', isinstance(man2, MoreMan)) print('man2是類Person的實例嗎', isinstance(man2, Person)) man3 = MoreMan.fromBirthYearStaticmethod('John', 1985) print('使用靜態方法') man3.display() print('man3是類Man的實例嗎', isinstance(man3, Man)) print('man3是類MoreMan的實例嗎', isinstance(man3, MoreMan)) print('man3是類Person的實例嗎', isinstance(man3, Person))
使用類方法 John's age is: 34 man是類Man的實例嗎 True man是類Person的實例嗎 True 使用靜態方法 John's age is: 34 man1是類Man的實例嗎 False man1是類Person的實例嗎 True 使用類方法 John's age is: 34 man2是類Man的實例嗎 True man2是類MoreMan的實例嗎 True man2是類Person的實例嗎 True 使用靜態方法 John's age is: 34 man3是類Man的實例嗎 False man3是類MoreMan的實例嗎 False man3是類Person的實例嗎 True
從上面的輸出就能夠看出來了,雖然靜態方法也能夠實現功能,但在繼承的時候卻沒有繼承給子類。因此若是一個方法須要被繼承的子類使用的話仍是用類方法。相反,當咱們不但願類的子類更改/重寫方法的特定實現時,就用靜態方法。ui
https://www.programiz.com/python-programming/methods/built-in/classmethod
實例方法,類方法,靜態方法的綜合比較,異同見代碼註釋。
class C(): # instance method def ins(self): print("instance method") @staticmethod def sta(): print("static method") @classmethod def cla(cls, c_arg): print("class method %s" % c_arg) # 實例化類C() ins_cls = C() # 實例方法只能用實例調用,不可用直接用類調用,會報錯:「TypeError: ins() missing 1 required positional argument: 'self'」 # C.ins() ins_cls.ins() # 靜態方法能夠經過類調用,也能夠經過實例調用 C.sta() ins_cls.sta() # 類方法能夠經過類調用,也能夠經過實例調用cla(cls)中的參數cls就是調用它的類自己,cls跟self差很少,用的時候只需傳實際參數c_arg便可 C.cla("class_arg") ins_cls.cla("class_arg")
instance method static method static method class method class method