成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

如何正確地使用Python的屬性和描述符

huayeluoliuhen / 469人閱讀

摘要:描述符登場(chǎng)什么是描述符一般來說,描述符是一個(gè)具有綁定行為的對(duì)象屬性,其屬性的訪問被描述符協(xié)議方法覆寫。先看如何用描述符來解決上面邏輯重復(fù)的問題。

關(guān)于@property裝飾器

在Python中我們使用@property裝飾器來把對(duì)函數(shù)的調(diào)用偽裝成對(duì)屬性的訪問。

那么為什么要這樣做呢?因?yàn)锧property讓我們將自定義的代碼同變量的訪問/設(shè)定聯(lián)系在了一起,同時(shí)為你的類保持一個(gè)簡(jiǎn)單的訪問屬性的接口。

舉個(gè)栗子,假如我們有一個(gè)需要表示電影的類:

class Movie(object):
    def __init__(self, title, description, score, ticket):
        self.title = title
        self.description = description
        self.score = scroe
        self.ticket = ticket
  

你開始在項(xiàng)目的其他地方使用這個(gè)類,但是之后你意識(shí)到:如果不小心給電影打了負(fù)分怎么辦?你覺得這是錯(cuò)誤的行為,希望Movie類可以阻止這個(gè)錯(cuò)誤。 你首先想到的辦法是將Movie類修改為這樣:

class Movie(object):
    def __init__(self, title, description, score, ticket):
        self.title = title
        self.description = description
     self.ticket = ticket
        if score < 0:
            raise ValueError("Negative value not allowed:{}".format(score))
        self.score = scroe

但這行不通。因?yàn)槠渌糠值拇a都是直接通過Movie.score來賦值的。這個(gè)新修改的類只會(huì)在__init__方法中捕獲錯(cuò)誤的數(shù)據(jù),但對(duì)于已經(jīng)存在的類實(shí)例就無能為力了。如果有人試著運(yùn)行m.scrore= -100,那么誰也沒法阻止。那該怎么辦?

Python的property解決了這個(gè)問題。

我們可以這樣做

class Movie(object):
    def __init__(self, title, description, score):
        self.title = title
        self.description = description
        self.score = score
     self.ticket = ticket

    @property
    def score(self):
        return self.__score


    @score.setter
    def score(self, score):
        if score < 0:
            raise ValueError("Negative value not allowed:{}".format(score))
        self.__score = score

    @score.deleter
    def score(self):
        raise AttributeError("Can not delete score")

這樣在任何地方修改score都會(huì)檢測(cè)它是否小于0。

property的不足

對(duì)property來說,最大的缺點(diǎn)就是它們不能重復(fù)使用。舉個(gè)例子,假設(shè)你想為ticket字段也添加非負(fù)檢查。下面是修改過的新類:

class Movie(object):
    def __init__(self, title, description, score, ticket):
        self.title = title
        self.description = description
        self.score = score
        self.ticket = ticket

    @property
    def score(self):
        return self.__score


    @score.setter
    def score(self, score):
        if score < 0:
            raise ValueError("Negative value not allowed:{}".format(score))
        self.__score = score

    @score.deleter
    def score(self):
        raise AttributeError("Can not delete score")


    @property
    def ticket(self):
        return self.__ticket

    @ticket.setter
    def ticket(self, ticket):
        if ticket < 0:
            raise ValueError("Negative value not allowed:{}".format(ticket))
        self.__ticket = ticket


    @ticket.deleter
    def ticket(self):
        raise AttributeError("Can not delete ticket")

可以看到代碼增加了不少,但重復(fù)的邏輯也出現(xiàn)了不少。雖然property可以讓類從外部看起來接口整潔漂亮,但是卻做不到內(nèi)部同樣整潔漂亮。

描述符登場(chǎng)

什么是描述符?

一般來說,描述符是一個(gè)具有綁定行為的對(duì)象屬性,其屬性的訪問被描述符協(xié)議方法覆寫。這些方法是__get__()、__set__()和__delete__(),一個(gè)對(duì)象中只要包含了這三個(gè)方法中的至少一個(gè)就稱它為描述符。

描述符有什么作用?

The default behavior for attribute access is to get, set, or delete the attribute from an object’s dictionary. For instance, a.x has a lookup chain starting witha.__dict__["x"], then type(a).__dict__["x"], and continuing through the base classes of type(a) excluding metaclasses. If the looked-up value is an object defining one of the descriptor methods, then Python may override the default behavior and invoke the descriptor method instead. Where this occurs in the precedence chain depends on which descriptor methods were defined.-----摘自官方文檔

簡(jiǎn)單的說描述符會(huì)改變一個(gè)屬性的基本的獲取、設(shè)置和刪除方式。

先看如何用描述符來解決上面 property邏輯重復(fù)的問題。

class Integer(object):
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, owner):
       return instance.__dict__[self.name]

    def __set__(self, instance, value):
        if value < 0:
            raise ValueError("Negative value not allowed")
        instance.__dict__[self.name] = value

class Movie(object):
    score = Integer("score")
    ticket = Integer("ticket")

因?yàn)槊枋龇麅?yōu)先級(jí)高并且會(huì)改變默認(rèn)的get、set行為,這樣一來,當(dāng)我們?cè)L問或者設(shè)置Movie().score的時(shí)候都會(huì)受到描述符Integer的限制。

不過我們也總不能用下面這樣的方式來創(chuàng)建實(shí)例。

a = Movie()
a.score = 1
a.ticket = 2
a.title = "test"
a.descript = "..."

這樣太生硬了,所以我們還缺一個(gè)構(gòu)造函數(shù)。

class Integer(object):
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        if value < 0:
            raise ValueError("Negative value not allowed")
        instance.__dict__[self.name] = value


class Movie(object):
    score = Integer("score")
    ticket = Integer("ticket")
    
    def __init__(self, title, description, score, ticket):
        self.title = title
        self.description = description
        self.score = score
        self.ticket = ticket

這樣在獲取、設(shè)置和刪除score和ticket的時(shí)候都會(huì)進(jìn)入Integer的__get__、__set__,從而減少了重復(fù)的邏輯。

現(xiàn)在雖然問題得到了解決,但是你可能會(huì)好奇這個(gè)描述符到底是如何工作的。具體來說,在__init__函數(shù)里訪問的是自己的self.score和self.ticket,怎么和類屬性score和ticket關(guān)聯(lián)起來的?

描述符如何工作

看官方的說明

If an object defines both __get__() and __set__(), it is considered a data descriptor. Descriptors that only define __get__() are called non-data descriptors (they are typically used for methods but other uses are possible).

Data and non-data descriptors differ in how overrides are calculated with respect to entries in an instance’s dictionary. If an instance’s dictionary has an entry with the same name as a data descriptor, the data descriptor takes precedence. If an instance’s dictionary has an entry with the same name as a non-data descriptor, the dictionary entry takes precedence.

The important points to remember are:

descriptors are invoked by the __getattribute__() method
overriding __getattribute__() prevents automatic descriptor calls
object.__getattribute__() and type.__getattribute__() make different calls to __get__().
data descriptors always override instance dictionaries.
non-data descriptors may be overridden by instance dictionaries.
類調(diào)用__getattribute__()的時(shí)候大概是下面這樣子:

def __getattribute__(self, key):
    "Emulate type_getattro() in Objects/typeobject.c"
    v = object.__getattribute__(self, key)
    if hasattr(v, "__get__"):
        return v.__get__(None, self)
    return v

  

下面是摘自國(guó)外一篇博客上的內(nèi)容。

Given a Class “C” and an Instance “c” where “c = C(…)”, calling “c.name” means looking up an Attribute “name” on the Instance “c” like this:

Get the Class from Instance
Call the Class’s special method getattribute__. All objects have a default __getattribute
Inside getattribute

Get the Class’s mro as ClassParents
For each ClassParent in ClassParents
If the Attribute is in the ClassParent’s dict
If is a data descriptor
Return the result from calling the data descriptor’s special method __get__()
Break the for each (do not continue searching the same Attribute any further)
If the Attribute is in Instance’s dict
Return the value as it is (even if the value is a data descriptor)
For each ClassParent in ClassParents
If the Attribute is in the ClassParent’s dict
If is a non-data descriptor
Return the result from calling the non-data descriptor’s special method __get__()
If it is NOT a descriptor
Return the value
If Class has the special method getattr
Return the result from calling the Class’s special method__getattr__.
我對(duì)上面的理解是,訪問一個(gè)實(shí)例的屬性的時(shí)候是先遍歷它和它的父類,尋找它們的__dict__里是否有同名的data descriptor如果有,就用這個(gè)data descriptor代理該屬性,如果沒有再尋找該實(shí)例自身的__dict__,如果有就返回。任然沒有再查找它和它父類里的non-data descriptor,最后查找是否有__getattr__

描述符的應(yīng)用場(chǎng)景

python的property、classmethod修飾器本身也是一個(gè)描述符,甚至普通的函數(shù)也是描述符(non-data discriptor)

django model和SQLAlchemy里也有描述符的應(yīng)用

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    email = db.Column(db.String(120), unique=True)

    def __init__(self, username, email):
        self.username = username
        self.email = email

    def __repr__(self):
        return "" % self.username
  

后記

只有當(dāng)確實(shí)需要在訪問屬性的時(shí)候完成一些額外的處理任務(wù)時(shí),才應(yīng)該使用property。不然代碼反而會(huì)變得更加啰嗦,而且這樣會(huì)讓程序變慢很多。

參考文章:

https://docs.python.org/3.5/h...

http://www.betterprogramming....

http://stackoverflow.com/ques...

http://www.jianshu.com/p/250f...

http://www.geekfan.net/7862/

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/45503.html

相關(guān)文章

  • [譯] 屬性訪問、特性描述 2

    摘要:不像其他屬性,描述符在類級(jí)別上創(chuàng)建。當(dāng)所有者類被定義時(shí),每個(gè)描述符對(duì)象都是被綁定到一個(gè)不同的類級(jí)別屬性的描述符類實(shí)例。這必須返回描述符的值。此外,描述符對(duì)有一個(gè)方便的響應(yīng)和請(qǐng)求格式。 注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python __getattribute__()方法 __getattribute__()方法是...

    CloudwiseAPM 評(píng)論0 收藏0
  • python屬性描述

    摘要:作為一個(gè)小白,每天都在不斷地看東西,學(xué)知識(shí),今天給大家介紹一個(gè)好東西屬性描述符什么是屬性描述符呢其實(shí)在一個(gè)類中實(shí)現(xiàn)中任意一個(gè)魔法函數(shù)就是一個(gè)屬性描述符。接下來我們定義一個(gè)屬性描述符當(dāng)我們用類或者實(shí)例來調(diào)用該屬性時(shí),會(huì)返回函數(shù)的結(jié)果。 作為一個(gè)小白,每天都在不斷地看東西,學(xué)知識(shí),今天給大家介紹一個(gè)好東西——屬性描述符什么是屬性描述符呢?其實(shí)在一個(gè)類中實(shí)現(xiàn)set__、__get__、__d...

    alin 評(píng)論0 收藏0
  • [譯] Python 學(xué)習(xí) —— __init__() 方法 1

    摘要:第一是在對(duì)象生命周期中初始化是最重要的一步每個(gè)對(duì)象必須正確初始化后才能正常工作。第二是參數(shù)值可以有多種形式?;悓?duì)象的方法對(duì)象生命周期的基礎(chǔ)是它的創(chuàng)建初始化和銷毀。在某些情況下,這種默認(rèn)行為是可以接受的。 注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python __init__()方法意義重大的原因有兩個(gè)。第一是在對(duì)象生命...

    MobService 評(píng)論0 收藏0
  • JS 裝飾器,一篇就夠

    摘要:的裝飾器中的同樣借鑒了這個(gè)語法糖,不過依賴于的方法。等同于也就是說,裝飾器是一個(gè)對(duì)類進(jìn)行處理的函數(shù)。別名或裝飾器在控制臺(tái)顯示一條警告,表示該方法將廢除。有了裝飾器,就可以改寫上面的代碼。 更多文章,請(qǐng)?jiān)贕ithub blog查看 在 ES6 中增加了對(duì)類對(duì)象的相關(guān)定義和操作(比如 class 和 extends ),這就使得我們?cè)诙鄠€(gè)不同類之間共享或者擴(kuò)展一些方法或者行為的時(shí)候,變得并...

    learning 評(píng)論0 收藏0
  • [譯] 屬性訪問、特性描述 1

    摘要:許多程序員發(fā)現(xiàn)賦值語句比方法函數(shù)看起來更清晰。自從和屬性的創(chuàng)建來自,我們必須經(jīng)常定義特性使用如下代碼這允許我們用一條簡(jiǎn)單的語句添加一張牌到手中像下面這樣前面的賦值語句有一個(gè)缺點(diǎn),因?yàn)樗雌饋硐褚粡埮铺娲怂械呐啤? 注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python 對(duì)象就是一些特性的集合,包括方法和屬性。object...

    褰辯話 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<