簡體   English   中英

foo.bar()和bar(foo)之間的區別?

[英]Difference between foo.bar() and bar(foo)?

考慮:

class Parent():
    def __init__(self, last_name, eye_color):
        self.last_name = last_name
        self.eye_color = eye_color

    def show_info(self):
        print("Last Name - "+self.last_name)
        print("Eye Color - "+self.eye_color)

billy_cyrus = Parent("Cyrus", "blue")

以上內容來自Udacity Python課程。 我發現我可以使用以下任一方法調用show_info ,例如billy_cyrus

billy_cyrus.show_info()
Parent.show_info(billy_cyrus)

我很好奇為什么。 這兩種方法有區別嗎? 如果是這樣的話什么時候會用到另一個呢? 如果重要的話,我正在使用Python 3.6。

就調用方法而言,大部分時間都沒有區別。 就底層機械如何工作而言,存在一點差異。

由於show_info是一個方法 ,因此它是類中的描述符 這意味着當你通過一個沒有被另一個屬性遮蔽的實例訪問它時, . operator在描述符上調用__get__以為該實例創建綁定方法。 綁定方法基本上是一個閉包,它在您提供的任何其他參數之前為您傳遞self參數。 您可以看到綁定發生如下:

>>> billy_cyrus.show_info
<bound method Parent.show_info of <__main__.Parent object at 0x7f7598b14be0>>

每次使用時都會創建一個不同的閉包. 類方法的運算符。

另一方面,如果通過類對象訪問該方法,則它不會受到約束。 該方法是一個描述符,它只是該類的常規屬性:

>>> Parent.show_info
<function __main__.Parent.show_info>

在調用方法之前,您可以通過自己調用__get__來模擬綁定方法的確切行為:

>>> bound_meth = Parent.show_info.__get__(billy_cyrus, type(billy_cyrus))
>>> bound_meth
<bound method Parent.show_info of <__main__.Parent object at 0x7f7598b14be0>>

同樣,這對99.99%的情況沒有任何影響,因為函數bound_meth()Parent.bound_meth(billy_cyrus)最終調用具有相同參數的相同底層函數對象。

哪里重要

有幾個地方重要的是如何調用類方法。 一個常見的用例是覆蓋方法,但希望使用父類中提供的定義。 例如,假設我有一個類,我通過覆蓋__setattr__使其成為“不可變的”。 我仍然可以在實例上設置屬性,如下面顯示的__init__方法:

class Test:
    def __init__(self, a):
        object.__setattr__(self, 'a', a)
    def __setattr__(self, name, value):
        raise ValueError('I am immutable!')

如果我試圖通過self.a = a__init__self.a = a進行正常調用, __setattr__每次都會引發一個ValueError 但是通過使用object.__setattr__ ,我可以繞過這個限制。 或者,我可以為相同的效果執行super().__setattr__('a', a) ,或者對於非常相似的self.__dict__['a'] = a

@Silvio Mayolo的答案有另一個很好的例子,你有意將class方法用作可以應用於許多對象的函數。

另一個重要的地方(雖然不是在調用方法方面),是你使用其他常見的描述符,如property 與方法不同,屬性是數據描述符 這意味着除了__get__之外,它們還定義了一個__set__方法(以及可選的__delete__ )。 屬性創建一個虛擬屬性,其getter和setter是任意復雜的函數,而不僅僅是簡單的賦值。 要正確使用屬性,您必須通過實例執行此操作。 例如:

class PropDemo:
    def __init__(self, x=0):
        self.x = x
    @property
    def x(self):
        return self.__dict__['x']
    @x.setter
    def x(self, value):
        if value < 0:
            raise ValueError('Not negatives, please!')
        self.__dict__['x'] = value

現在你可以做點什么了

>>> inst = PropDemo()
>>> inst.x
0
>>> inst.x = 3
>>> inst.x
3

如果您嘗試通過類訪問該屬性,則可以獲取基礎描述符對象,因為它將是未綁定的屬性:

>>> PropDemo.x
<property at 0x7f7598af00e8>

在一個側面說明,隱藏具有相同名稱的屬性在屬性__dict__是一個整潔的把戲,因為數據描述符的工作在一個類中__dict__實例中的王牌項目__dict__ ,即使實例__dict__項王牌非數據描述符類。

哪里可以變得怪異

您可以使用Python中的實例方法覆蓋類方法。 這意味着type(foo).bar(foo)foo.bar()根本不會調用相同的底層函數。 這與魔術方法無關,因為它們總是使用前一次調用,但它可以對普通方法調用產生很大的影響。

有幾種方法可以覆蓋實例上的方法 我覺得最直觀的一個是將實例屬性設置為綁定方法。 以下是修改后的billy_cyrus ,假設原始問題中包含Parent的定義:

def alt_show_info(self):
    print('Another version of', self)

billy_cyrus.show_info = alt_show_info.__get__(billy_cyrus, Parent)

在這種情況下,調用實例上的方法與類將產生完全不同的結果。 這只能起作用,因為方法是非數據描述符。 如果它們是數據描述符(使用__set__方法),則賦值billy_cyrus.show_info = alt_show_info.__get__(billy_cyrus, Parent)不會覆蓋任何內容,而只是重定向到__set__ ,並在b billy_cyrus__dict__手動設置它只會讓它被忽略,就像財產一樣。

其他資源

以下是關於描述符的幾個資源:

兩者之間沒有語義差異。 這完全是一種風格問題。 您通常會在正常使用中使用billy_cyrus.show_info() ,但允許第二種方法的事實允許您使用Parent.show_info將該方法作為第一類對象本身。 如果不允許這樣做,那么做這樣的事情是不可能的(或者至少是相當困難的)。

function = Parent.show_info
so_many_billy_cyrus = [billy_cyrus, billy_cyrus, billy_cyrus]
map(function, so_many_billy_cyrus)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM