国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

聽說你會 Python ?

Raaabbit / 2552人閱讀

摘要:描述符我想寫一個類,其屬性為的整數,若賦值時不在此范圍內則拋出異常,我決定用描述符來實現這個需求。回到之前的問題,我們即使在將具體的屬性寫入實例字典中,但是由于類字典中存在著,因此,我們在調用屬性時,依舊會觸發描述符協議。

前言

最近覺得 Python 太“簡單了”,于是在朋友面前放肆了一把:“我覺得 Python 是世界上最簡單的語言!”。朋友嘴角閃過了一絲輕蔑的微笑(內心 OS:Naive!,作為一個 Python 開發者,我必須要給你一點人生經驗,不然你不知道天高地厚!)于是朋友給我了一份滿分 100 分的題,然后這篇文章就是記錄下做這套題所踩過的坑。

1.列表生成器

描述

下面的代碼會報錯,為什么?

class A(object):
    x = 1
    gen = (x for _ in xrange(10))  # gen=(x for _ in range(10))


if __name__ == "__main__":
    print(list(A.gen))

答案

這個問題是變量作用域問題,在 gen=(x for _ in xrange(10)) 中 gen 是一個 generator ,在 generator 中變量有自己的一套作用域,與其余作用域空間相互隔離。因此,將會出現這樣的 NameError: name "x" is not defined 的問題,那么解決方案是什么呢?答案是:用 lambda 。

class A(object):
    x = 1
    gen = (lambda x: (x for _ in xrange(10)))(x)  # gen=(x for _ in range(10))


if __name__ == "__main__":
    print(list(A.gen))

或者

class A(object):
    x = 1
    gen = (A.x for _ in xrange(10))  # gen=(x for _ in range(10))


if __name__ == "__main__":
    print(list(A.gen))

補充

感謝評論區幾位提出的意見,這里我給一份官方文檔的說明吧:
The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods – this includes comprehensions and generator expressions since they are implemented using a function scope. This means that the following will fail:

class A:
    a = 42
    b = list(a + i for i in range(10))

參考鏈接 Python2 Execution-Model:Naming-and-Binding , Python3 Execution-Model:Resolution-of-Names。據說這是 PEP 227 中新增的提案,我回去會進一步詳細考證。再次拜謝評論區 @沒頭腦很著急 @涂偉忠 @Cholerae 三位的勘誤指正。

2.裝飾器

描述

我想寫一個類裝飾器用來度量函數/方法運行時間

import time

class Timeit(object):
    def __init__(self, func):
        self._wrapped = func

    def __call__(self, *args, **kws):
        start_time = time.time()
        result = self._wrapped(*args, **kws)
        print("elapsed time is %s " % (time.time() - start_time))
        return result

這個裝飾器能夠運行在普通函數上:

@Timeit
def func():
    time.sleep(1)
    return "invoking function func"


if __name__ == "__main__":
    func()  # output: elapsed time is 1.00044410133

但是運行在方法上會報錯,為什么?

@Timeit
def func():
    time.sleep(1)
    return "invoking function func"


if __name__ == "__main__":
    func()  # output: elapsed time is 1.00044410133

如果我堅持使用類裝飾器,應該如何修改?

答案

使用類裝飾器后,在調用 func 函數的過程中其對應的 instance 并不會傳遞給?call?方法,造成其 mehtod unbound ,那么解決方法是什么呢?描述符賽高

class Timeit(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("invoking Timer")

    def __get__(self, instance, owner):
        return lambda *args, **kwargs: self.func(instance, *args, **kwargs)
3.Python 調用機制

描述

我們知道 call 方法可以用來重載圓括號調用,好的,以為問題就這么簡單?Naive!

class A(object):
    def __call__(self):
        print("invoking __call__ from A!")


if __name__ == "__main__":
    a = A()
    a()  # output: invoking __call__ from A

現在我們可以看到 a() 似乎等價于 a.__call__() ,看起來很 Easy 對吧,好的,我現在想作死,又寫出了如下的代碼,

a.__call__ = lambda: "invoking __call__ from lambda"
a.__call__()
# output:invoking __call__ from lambda
a()


# output:invoking __call__ from A!

請大佬們解釋下,為什么 a() 沒有調用出 a.__call__() (此題由 USTC 王子博前輩提出)

答案
原因在于,在 Python 中,新式類( new class )的內建特殊方法,和實例的屬性字典是相互隔離的,具體可以看看 Python 官方文檔對于這一情況的說明

For new-style classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary. That behaviour is the reason why the following code raises an exception (unlike the equivalent example with old-style classes):

同時官方也給出了一個例子:

class C(object):
    pass


c = C()
c.__len__ = lambda: 5
len(c)


# Traceback (most recent call last):
#  File "", line 1, in 
# TypeError: object of type "C" has no len()

回到我們的例子上來,當我們在執行 a.__call__=lambda:"invoking call from lambda" 時,的確在我們在 a.__dict__ 中新增加了一個 key 為 call 的 item,但是當我們執行 a() 時,因為涉及特殊方法的調用,因此我們的調用過程不會從 a.__dict__ 中尋找屬性,而是從 tyee(a).__dict__ 中尋找屬性。因此,就會出現如上所述的情況。

4.描述符

我想寫一個 Exam 類,其屬性 math 為 [0,100] 的整數,若賦值時不在此范圍內則拋出異常,我決定用描述符來實現這個需求。

class Grad(object):
    def __init__(self):
        self._grade_pool = {}

    def __get__(self, instance, owner):
        return self._grade_pool.get(instance, None)

    def __set__(self, instance, value):
        if 0 <= value <= 100:
            _grade_pool = self.__dict__.setdefault("_grade_pool", {})
            _grade_pool[instance] = value
        else:
            raise ValueError("fuck")

答案
1.第一個問題的其實很簡單,如果你再運行一次 print(niche.math) 你就會發現,輸出值是 75 ,那么這是為什么呢?這就要先從 Python 的調用機制說起了。我們如果調用一個屬性,那么其順序是優先從實例的 dict 里查找,然后如果沒有查找到的話,那么一次查詢類字典,父類字典,直到徹底查不到為止。好的,現在回到我們的問題,我們發現,在我們的類 Exam 中,其 self.math 的調用過程是,首先在實例化后的實例的 dict 中進行查找,沒有找到,接著往上一級,在我們的類 Exam 中進行查找,好的找到了,返回。那么這意味著,我們對于 self.math 的所有操作都是對于類變量 math 的操作。因此造成變量污染的問題。那么該則怎么解決呢?很多同志可能會說,恩,在 set 函數中將值設置到具體的實例字典不就行了。
那么這樣可不可以呢?答案是,很明顯不得行啊,至于為什么,就涉及到我們 Python 描述符的機制了,描述符指的是實現了描述符協議的特殊的類,三個描述符協議指的是 get , ‘set‘ , delete 以及 Python 3.6 中新增的 set_name 方法,其中實現了 get 以及 set / delete / set_name 的是 Data descriptors ,而只實現了 get 的是 Non-Data descriptor 。那么有什么區別呢,前面說了, 我們如果調用一個屬性,那么其順序是優先從實例的 dict 里查找,然后如果沒有查找到的話,那么一次查詢類字典,父類字典,直到徹底查不到為止。 但是,這里沒有考慮描述符的因素進去,如果將描述符因素考慮進去,那么正確的表述應該是我們如果調用一個屬性,那么其順序是優先從實例的 dict 里查找,然后如果沒有查找到的話,那么一次查詢類字典,父類字典,直到徹底查不到為止。其中如果在類實例字典中的該屬性是一個 Data descriptors ,那么無論實例字典中存在該屬性與否,無條件走描述符協議進行調用,在類實例字典中的該屬性是一個 Non-Data descriptors ,那么優先調用實例字典中的屬性值而不觸發描述符協議,如果實例字典中不存在該屬性值,那么觸發 Non-Data descriptor 的描述符協議。回到之前的問題,我們即使在 set 將具體的屬性寫入實例字典中,但是由于類字典中存在著 Data descriptors ,因此,我們在調用 math 屬性時,依舊會觸發描述符協議。

2.經過改良的做法,利用 dict 的 key 唯一性,將具體的值與實例進行綁定,但是同時帶來了內存泄露的問題。那么為什么會造成內存泄露呢,首先復習下我們的 dict 的特性,dict 最重要的一個特性,就是凡可 hash 的對象皆可為 key ,dict 通過利用的 hash 值的唯一性(嚴格意義上來講并不是唯一,而是其 hash 值碰撞幾率極小,近似認定其唯一)來保證 key 的不重復性,同時(敲黑板,重點來了),dict 中的 key 引用是強引用類型,會造成對應對象的引用計數的增加,可能造成對象無法被 gc ,從而產生內存泄露。那么這里該怎么解決呢?兩種方法
第一種:

class Grad(object):
    def __init__(self):
        import weakref
        self._grade_pool = weakref.WeakKeyDictionary()

    def __get__(self, instance, owner):
        return self._grade_pool.get(instance, None)

    def __set__(self, instance, value):
        if 0 <= value <= 100:
            _grade_pool = self.__dict__.setdefault("_grade_pool", {})
            _grade_pool[instance] = value
        else:
            raise ValueError("fuck")

weakref 庫中的 WeakKeyDictionary 所產生的字典的 key 對于對象的引用是弱引用類型,其不會造成內存引用計數的增加,因此不會造成內存泄露。同理,如果我們為了避免 value 對于對象的強引用,我們可以使用 WeakValueDictionary 。
第二種:在 Python 3.6 中,實現的 PEP 487 提案,為描述符新增加了一個協議,我們可以用其來綁定對應的對象:

class Grad(object):
    def __get__(self, instance, owner):
        return instance.__dict__[self.key]

    def __set__(self, instance, value):
        if 0 <= value <= 100:
            instance.__dict__[self.key] = value
        else:
            raise ValueError("fuck")

    def __set_name__(self, owner, name):
        self.key = name

這道題涉及的東西比較多,這里給出一點參考鏈接,invoking-descriptors , Descriptor HowTo Guide , PEP 487 , what`s new in Python 3.6 。

5.Python 繼承機制

描述

試求出以下代碼的輸出結果。

class Init(object):
    def __init__(self, value):
        self.val = value


class Add2(Init):
    def __init__(self, val):
        super(Add2, self).__init__(val)
        self.val += 2


class Mul5(Init):
    def __init__(self, val):
        super(Mul5, self).__init__(val)
        self.val *= 5


class Pro(Mul5, Add2):
    pass


class Incr(Pro):
    csup = super(Pro)

    def __init__(self, val):
        self.csup.__init__(val)
        self.val += 1


p = Incr(5)
print(p.val)

答案
輸出是 36 ,具體可以參考 New-style Classes , multiple-inheritance

6. Python 特殊方法

描述

我寫了一個通過重載 new 方法來實現單例模式的類。

class Singleton(object):
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance:
            return cls._instance
        cls._isntance = cv = object.__new__(cls, *args, **kwargs)
        return cv


sin1 = Singleton()
sin2 = Singleton()
print(sin1 is sin2)
# output: True

現在我有一堆類要實現為單例模式,所以我打算照葫蘆畫瓢寫一個元類,這樣可以讓代碼復用:

class SingleMeta(type):
    def __init__(cls, name, bases, dict):
        cls._instance = None
        __new__o = cls.__new__

        def __new__(cls, *args, **kwargs):
            if cls._instance:
                return cls._instance
            cls._instance = cv = __new__o(cls, *args, **kwargs)
            return cv

        cls.__new__ = __new__


class A(object):
    __metaclass__ = SingleMeta


a1 = A()  # what`s the fuck

哎呀,好氣啊,為啥這會報錯啊,我明明之前用這種方法給 getattribute 打補丁的,下面這段代碼能夠捕獲一切屬性調用并打印參數

class TraceAttribute(type):
    def __init__(cls, name, bases, dict):
        __getattribute__o = cls.__getattribute__

        def __getattribute__(self, *args, **kwargs):
            print("__getattribute__:", args, kwargs)
            return __getattribute__o(self, *args, **kwargs)

        cls.__getattribute__ = __getattribute__


class A(object):  # Python 3 是 class A(object,metaclass=TraceAttribute):
    __metaclass__ = TraceAttribute
    a = 1
    b = 2


a = A()
a.a
# output: __getattribute__:("a",){}
a.b

試解釋為什么給 getattribute 打補丁成功,而 new 打補丁失敗。
如果我堅持使用元類給 new 打補丁來實現單例模式,應該怎么修改?

答案

其實這是最氣人的一點,類里的 new 是一個 staticmethod 因此替換的時候必須以 staticmethod 進行替換。答案如下:

class SingleMeta(type):
    def __init__(cls, name, bases, dict):
        cls._instance = None
        __new__o = cls.__new__

        @staticmethod
        def __new__(cls, *args, **kwargs):
            if cls._instance:
                return cls._instance
            cls._instance = cv = __new__o(cls, *args, **kwargs)
            return cv

        cls.__new__ = __new__


class A(object):
    __metaclass__ = SingleMeta


print(A() is A())  # output: True
結語

感謝師父大人的一套題讓我開啟新世界的大門,恩,博客上沒法艾特,只能傳遞心意了。說實話 Python 的動態特性可以讓其用眾多 black magic 去實現一些很舒服的功能,當然這也對我們對語言特性及坑的掌握也變得更嚴格了,愿各位 Pythoner 沒事閱讀官方文檔,早日達到裝逼如風,常伴吾身的境界。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/45012.html

相關文章

  • 數據工程師妹子養成手記——數據庫篇

    摘要:是什么呀是一個和不太一樣的數據庫。懷疑是同時聯了四個集合的數據造成的。這本書的定位是和的應用,所以有意弱化了數據庫的搭建維護和底層優化。所以本書可能不適合數據庫工程師。 這篇文章沒有代碼,請放心閱讀。 程序員最寶貴的東西是生命,生命屬于程序員只有一次。一個程序員的一生應該這樣度過:當她回首往事的時候,她不會因為搭建環境浪費時間而悔恨,也不會因為集群無法運行而羞恥。這樣,在她開發的時候,...

    yexiaobai 評論0 收藏0
  • 數據工程師妹子養成手記——數據庫篇

    摘要:是什么呀是一個和不太一樣的數據庫。懷疑是同時聯了四個集合的數據造成的。這本書的定位是和的應用,所以有意弱化了數據庫的搭建維護和底層優化。所以本書可能不適合數據庫工程師。 這篇文章沒有代碼,請放心閱讀。 程序員最寶貴的東西是生命,生命屬于程序員只有一次。一個程序員的一生應該這樣度過:當她回首往事的時候,她不會因為搭建環境浪費時間而悔恨,也不會因為集群無法運行而羞恥。這樣,在她開發的時候,...

    atinosun 評論0 收藏0
  • Python代碼來下載任意指定網易云歌曲(超詳細版)

    摘要:至于怎樣下載付費網易云音樂,還是開個會員吧,要知道免費是最貴的的這個道理。代碼寫完了,那還等什么,運行一下裝逼啊這下好了,裝逼失敗,被網易云認出來我是爬蟲的,那我試試加下請求頭結果加了還是一個樣,這個也算是個巨坑吧。 前兩天教了大家如何在控制臺上找到真實的mp3播放地址,但是不可以下載付費的,因為只能下載可播放的歌曲。至于怎樣下載付費網易云音樂,還是開個會員吧,要知道免費是最貴的的這個...

    wwolf 評論0 收藏0
  • 10個Python Pandas技巧,使您的工作更有效率

    摘要:是一個廣泛用于結構化數據的包。因此,的任何變化都會導致發生變化。這是檢查值分布的命令。這也是每個人都會使用的命令。我想在這里指出兩個技巧。另一個技巧是處理混合在一起的整數和缺失值。將所有浮點數舍入為整數。 showImg(https://segmentfault.com/img/remote/1460000019138448?w=432&h=270); Pandas是一個廣泛用于結構化...

    stormjun 評論0 收藏0
  • 在線編程練習實踐網站

    摘要:在此收集一些自己遇到的一些在線練習的網站,當然大部分是。建議邊學習邊編程,學習編程是不能光看不實踐的。國外的一個練習網站,有,也有,每種語言都有自己的道場,每個用戶都有不同的等級,刷題提高等級,也可以插卡別人優秀的解決方案。 在學習的過程中會發現很多知識點如果不在工作中運用或者手寫帶驗證的話,很容易忘記。任何技能的掌握都是需要不斷練習的。在此收集一些自己遇到的一些在線練習的網站,當然大...

    huhud 評論0 收藏0

發表評論

0條評論

Raaabbit

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<