![](/img/trans.png)
[英]In python, what is the difference between 'import foo.bar as bar' and 'from foo import bar'?
[英]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.