![](/img/trans.png)
[英]Is it possible to access exec-provided globals dictionary from within a function?
[英]Is it possible to delete an external object reference (variable) from within a function without using exec, eval, compile?
我相信我正在徹底了解Python如何使用變量和引用等,以及如何在函數之間傳遞它們。
由於Python做事的方式,如果我將引用變量傳遞給函數並嘗試刪除其中的變量,則:
class C(): pass
c = C()
def euthanize(patient): del patient
euthanize(c)
外部對象引用不會被刪除,因為該函數僅刪除了本地引用變量(該函數是在調用函數時創建的),而不是外部上下文中的引用變量:
assert c #No error
一個類也是如此:
class C():
def die(self): c.__del__() #same result for 'del c' here
def __del__(self): pass
c = C()
c.die()
assert c #No error
這使我相信,除了使用exec
或eval
類的方法外,沒有其他方法可以從創建變量的上下文之外刪除變量。 這個對嗎?
我確信我會得到很多“為什么要這么做呢?” 評論。 請不要。 我已經了解到,對於Python而言,如果沒有某種簡單明了的方法來完成一項任務,那么一開始我可能就不應該這樣做,而我應該研究另一種方法。 我問這個問題是因為我想確保我是對的,這是其中一種情況,因此大多數人不會發現他們需要完成的事情(當然,如果您確實需要這樣做,總是exec
!)。 我注意到人們非常迅速地問“您為什么要...?” 在這樣。 這是可以理解的 ,但有時很煩人。 請在這種情況下不要這樣做,好嗎?
一種方法,但可能不是您所想的:
>>> class C(): pass
...
>>> c = C()
>>> def euthanize(patient):
... del globals()[patient]
...
>>> euthanize('c')
>>> c
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'c' is not defined
另一種方式,不要求將患者作為字符串傳遞(但仍不能清除閉包等):
>>> def euthanize(patient):
... for p in [x[0] for x in
globals().items() if
x[1] is patient]:
... del globals()[p]
...
>>> c
<__main__.C object at 0x7fe8c4a3d518>
>>> euthanize(c)
>>> c
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'c' is not defined
您基本上是正確的,但我認為原因比您似乎認為的要廣泛。 正如您提到的鏈接中所討論的那樣,參數是通過“對象引用”傳遞的。 換句話說,當您執行euthanize(c)
, euthanize
僅獲取c
引用的對象; 它不知道您如何引用它。 在這種情況下,您使用了一個簡單的名稱c
,但是euthanize
不知道。 您可能根本沒有使用過變量。 如果您的示例是euthanize(C())
,那么您的示例將是等效的,在這種情況下,永遠不會有任何引用C實例的名稱。
所以,你不能刪除從封閉命名空間的“變量”的原因是,你正在嘗試刪除是在封閉的命名空間的名稱 ,而函數傳遞參數值只。 (不過,這不是按值傳遞,因為該值是對象引用,在鏈接中已提及該對象引用,因此我假設您已明白我的意思。)
您使用__del__
示例有點像鯡魚。 __del__
並沒有真正刪除任何內容。 如果沒有對對象的進一步引用,則可能會調用它,但是在第二個示例中調用del c
可能會或可能不會導致c.__del__
被調用(立即或以后)。 因此,即使在同一個全局命名空間中直接執行c = C()
然后執行c.__del__()
也不會刪除名為c
的變量(即名稱)。
正如ch3ka的答案所暗示的那樣,刪除名稱的唯一方法是將其作為字符串傳遞,然后使用它來執行諸如del globals(someName)
。 但是,即使這樣也不總是可行。 它適用於全局變量,但是例如,您不能在嵌套函數中使用它來刪除封閉函數的局部變量。
我想出了一種您可以進行類似操作的方式。 您必須使用類似容器或類屬性的東西。
例如:
def euthanize(patient,index): del patient[index]
class C: pass
L = [C()]
euthanize(L,0)
assert len(L) == 0
這與我在上面的問題中所問的不完全相同,但這是相似的,因為它確實刪除了對以上代碼中創建的對象L[0]
的外部強引用,並且該對象最終將被GC 。 可以使用weakref.finalize
進行測試:
import weakref
L = [C()]
weakref.finalize(L[0],lambda: print("L[0] finalized!"))
euthanize(L,0) #OUTPUT: L[0] finalized!
但是,確切地說,該函數不會刪除對象引用。 L
是對象參考。 該函數僅操縱L
編輯:並且正如BrenBarn在下面指出的那樣,如果還有其他對L
成員的引用,我試圖對此予以安樂死,則不會刪除這些引用。
使用class屬性的外觀如下:
c = type('C',(object,),{})()
setattr(c,'X',1)
def euthanizeX(obj): del obj.X
assert c.X #AttributeError (expected)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.