python內置裝飾器

前言

接着上一篇筆記,咱們來看看內置裝飾器propertystaticmethodclassmethodpython

1、property裝飾器

1. 普通方式修改屬性值
  • code
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__)
  • output
dirctly get: -280
to_fahrenheit: -472.0
{'temperature': -280}

這裏直接經過實例來修改類的屬性值,但從輸出能夠看到溫度超過了絕對零度-273.15卻沒有保錯,這是一個bug,下面來修復下。redis

2. 使用get_xxx, set_xxx方法修改類的屬性值
  • code
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__)
  • output
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

3. 使用@property裝飾器修改類的屬性值
  • code
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__)
  • output
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數據庫

2、staticmethod裝飾器(靜態方法)

靜態方法的用得不多,當須要一個不訪問類的任何屬性,但它屬於該類的實用函數時就能夠用靜態方法,它只處理傳入的參數。好比下面的靜態方法就單純接收一個參數date更改下它的鏈接符就返回了,但由於跟類Dates放一塊兒比較好歸類管理才放到類裏面的。能夠用類直接調用靜態方法,也能夠用類的實例調用靜態方法api

  • code
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")
  • output
Equal
Equal_1
  • 參考文章

https://www.programiz.com/python-programming/methods/built-in/staticmethoddom

3、classmethod裝飾器(類方法)

  • code
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()
  • output
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了。若是有類繼承基類的話,使用靜態方法建立的實例只屬於基類,繼承了基類的子類都不屬於實例,而使用類方法的話,繼承了基類的子類建立的實例,既屬於基類也屬於繼承了基類的子類。看下面代碼就懂了。函數

  • code
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))
  • output
使用類方法
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

4、綜合比較

實例方法,類方法,靜態方法的綜合比較,異同見代碼註釋。

  • code
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")
  • output
instance method
static method
static method
class method
class method

公衆號往期文章

  • python裝飾器
  • scrapy-redis debug視頻
  • scrapy-redis源碼淺析
  • scrapy過濾重複數據和增量爬取
  • redis基礎筆記
  • scrapy電影天堂實戰(二)建立爬蟲項目
  • scrapy電影天堂實戰(一)建立數據庫
  • scrapy基礎筆記
  • 在docker鏡像中加入環境變量
  • 筆記 | mongodb 入門操做
  • 筆記 | python元類
  • 筆記 | python2和python3使用super()
  • 那些你在python3中可能沒用到但應該用的東西
  • superset docker 部署
  • 開機啓動容器裏面的程序
  • 博客 | 三步部署hitchhiker-api
相關文章
相關標籤/搜索