簡體   English   中英

是否可以在不使用exec,eval,compile的情況下從函數中刪除外部對象引用(變量)?

[英]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

這使我相信,除了使用execeval類的方法外,沒有其他方法可以從創建變量的上下文之外刪除變量。 這個對嗎?

澄清

我確信我會得到很多“為什么要這么做呢?” 評論。 請不要。 我已經了解到,對於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.

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