我有兩個類,分別在不同的檔案中,a.py和b.py。
# a.py
import logging
LOG = logging.getLogger(__name__)
class A:
def name(self)。
# 做一些事情。
LOG.debug("Did something") # LOG讀取__name__并列印它。
我不能以任何方式修改a.py。
# b.py
import a
class B(A)。
pass pass
日志輸出是:
DEBUG:<TIME> called in a, Did something
我想改變__name__,以便一個子類的呼叫應該記錄called in b。
我的專案將其模塊名稱作為輸出的一部分進行記錄。然而,當我繼承一個類并呼叫父類的方法時,我無法知道它是從A或B呼叫的,因為它只顯示父類的模塊名稱。我怎樣才能改變它呢?或者,用其他的方法來避免這種情況?
uj5u.com熱心網友回復:
有一種方法可以做到,但它比你想象的要復雜。
__name__這個名字是怎么來的呢?答案是,它是一個全域變數。這是可以預期的,因為全域命名空間就是模塊的命名空間。例如:
>>> globals()['__name__']
'__main__'
如果你在python的Standard Type Hierarchy檔案中查找可呼叫程式,你會發現一個函式的__globals__屬性是只讀的。這意味著函式A.name將始終在模塊a的命名空間中查找其全域屬性(而不是說模塊命名空間本身是只讀的)。
改變一個函式的全域的唯一方法是復制該函式物件。這一點在Stack Overflow上至少已經被討論過幾次了:
這一點在Stack Overflow上至少已經被討論過幾次了。
復制類也出現了:
復制類?
解決方案1
我已經在我做的一個名為haggis的實用程式庫中實作了這兩種技術。具體來說,haggis.objects.copy_class將做你想要的事情:
from haggis.objects import copy_class>
import a
class B(copy_class(A, globals(), __name__) ) 。
pass。
這將復制A。所有的函式代碼和非函式屬性將被直接參考。然而,函式物件將有更新的__globals__和__module__屬性應用。
這個方法的主要問題是,它稍微打破了你所期望的繼承層次結構。在功能上,你不會看到任何區別,但是issubclass(b.B, a.A)將不再是真的。
解決方案 2(可能是最好的解決方案)
我可以看到復制所有 A的方法會有點麻煩,而且這樣做會破壞繼承的層次結構。幸運的是,還有一種方法,haggis.objects.copy_func,它可以幫助你只復制A中的name方法:
from haggis.objects import copy_func
import a
class B(A)。
name = copy_func(A.name, globals(), __name__)
這個解決方案更穩健一些,因為在python中復制類比復制函式要棘手得多。它是相當有效的,因為兩個版本的 name 實際上將共享相同的代碼物件:只有元資料,包括 __globals__,將有所不同。
解決方案3
如果你能稍微改變一下A,你就能得到一個更簡單的解決方案。python中的每一個(正常的)類都有一個__module__屬性,這個屬性是它所定義的模塊的__name__屬性。
你可以直接將A改寫為
class A。
def name(self)。
# 做一些事情。
print("Did in %s" % self.__module__)
使用self.__module__而不是__name__大致上類似于使用type(self)而不是__class__。
解決方案4
另一個簡單的想法是正確覆寫A.name:
class B(A)。
def name(self)。
# 做一些事情。
LOG.debug("Did something")
當然,我可以看到,如果# Do something是一個復雜的任務,沒有在其他地方實作,這將不能很好地作業。直接將函式物件從A中復制出來可能會更有效率。
uj5u.com熱心網友回復:
解決方案:
嘗試使用inspect.stack:
import inspect
class A:
def __init__(self):
pass.
def name(self)。
# 做一些事情。
print("Did in %s" % inspect.stack()[1] 。 filename.rpartition('/')[-1][:-3])
而現在運行b.py將輸出:
Did in b
解釋:
inspect可以檢查python腳本中的實時物件,如docs中所述:
為呼叫者的堆疊回傳一個幀記錄的串列。回傳串列中的第一個條目代表呼叫者;最后一個條目代表堆疊中最外層的呼叫。
還提到它輸出一個NamedTuple,如:
FrameInfo(frame, filename, lineno, function, code_context, index)
所以我們可以通過索引這個元組的第二個元素來獲得實時的檔案名,這將給出完整的路徑,所以我們必須把它分割成路徑并使用str.rpartition從完整路徑中提取檔案名。
編輯:
。對于不編輯a.py,你只能嘗試:
b.py:
import inspect
exec( inspect.getource(__import__('a'))
class B(A) 。
pass pass
而且它將給出:
Did in b
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/310499.html
標籤:
上一篇:在陣列/向量容器中存盤虛擬函式
