2013-03-03 50 views
19

我想從類中創建一個具有默認值參數的類中的方法。一般來說,我會過濾一些數據。在我的班級裏,我有一種方法,通常我會傳遞數據向量。有時候我沒有矢量,我會模擬數據。每次我沒有傳遞一個特定的向量時,我都會默認採用模擬數據。我認爲這應該是一個簡單的建築,在我的方法定義裏面我說a=self.vector。但由於某種原因,我有一個錯誤NameError: name 'self' is not defined。簡化的結構是:將類變量作爲默認值分配給類方法參數

class baseClass(object): # This class takes an initial data or simulation 
    def __init__(self): 
     self.x = 1 
     self.y = 2 

class extendedClass(baseClass): # This class does some filtering 
    def __init__(self): 
     baseClass.__init__(self) 
     self.z = 5 
    def doSomething(self, a=self.z): 
     self.z = 3 
     self.b = a 

if __name__ == '__main__': 
    a = extendedClass() 
    print a.__dict__ 
    a.doSomething() 
    print a.__dict__ 

我預期的輸出應該是:

{'y': 2, 'x': 1, 'z': 5} 
{'y': 2, 'x': 1, 'z': 3, 'b': 5} 

我試圖默認分配爲def doSomething(self, a=z):顯然它不會永遠工作。據我所知self.z是在這個範圍內可見,不應該是一個問題,它作爲默認值。不知道爲什麼我有這個錯誤,以及如何去做。這可能是一個簡單的問題,但我試圖找出解決方案,或者已經有一段時間沒有找到解決方案。我發現similar只針對其他語言的問題。

回答

25

你的理解是錯誤的。 self本身就是該函數定義的一個參數,所以在這一點上它不可能處於範圍之內。它只在函數本身的範圍內。

答案很簡單的說法默認爲None,然後檢查,該方法中:執行定義時

def doSomething(self, a=None): 
    if a is None: 
     a = self.z 
    self.z = 3 
    self.b = a 
+0

如果參數不能帶有虛假值,則應該只寫'if a:a = self.z'或甚至'a = a或self.z'。 – danijar 2016-05-16 10:34:56

+0

我想你的意思是'如果不是a:a = self.z' – BBischof 2017-04-07 18:05:05

+0

這是一個標準的pythonic模式,即有一個默認的類屬性,用作可選方法屬性的回退?或者,更改'extendedClass' __init__構造函數以包含一個可選的'z = None'屬性,並且如果'None'將其設置爲方法中的某些默認值,並且不將其作爲方法參數?我只能認爲,如果一個硬編碼的類屬性真的是一個常量,並且被多個方法需要,那麼它很有用,否則爲什麼不只是將'z'限制在方法的範圍內,可能沒有類或對象財產呢? – Davos 2017-10-05 15:13:10

6

下面是一個簡單示例模塊代碼的反彙編。代碼對象是字節碼的只讀容器,它使用的常量和名稱以及有關局部變量數量,所需堆棧大小等的元數據。請注意,所有代碼對象都編譯爲常量。這些是在編譯時創建的。但是對象class Afunction test在執行時被實例化(例如,當模塊被導入時)。

使課堂,BUILD_CLASS取名字'A',該基地tuple(object,),幷包含類的命名空間的屬性的dict。這就像通過調用type(name, bases, dict)來手動實例化類型。爲了使dict,從代碼對象A創建並調用一個函數。最後,類對象通過STORE_NAME存儲在模塊名稱空間中。

在代碼對象A中,self.z作爲參數加載到堆棧上,作爲參數MAKE_FUNCTION。字節碼操作LOAD_NAME將在當前本地語言(即正在定義的類名稱空間),模塊全局變量和內建函數中搜索self。如果self沒有在全局或內建範圍中定義,這顯然會失敗;它顯然沒有在本地範圍內定義。

但是,如果確實成功了,則將使用(self.z,)作爲其__defaults__屬性創建該功能,然後將其存儲到本地名稱test

>>> code = compile(''' 
... class A(object): 
... def test(self, a=self.z): pass 
... ''', '<input>', 'exec') 

>>> dis.dis(code) 
    2   0 LOAD_CONST    0 ('A') 
       3 LOAD_NAME    0 (object) 
       6 BUILD_TUPLE    1 
       9 LOAD_CONST    1 (<code object A ...>) 
      12 MAKE_FUNCTION   0 
      15 CALL_FUNCTION   0 
      18 BUILD_CLASS   
      19 STORE_NAME    1 (A) 
      22 LOAD_CONST    2 (None) 
      25 RETURN_VALUE 

>>> dis.dis(code.co_consts[1]) # code object A 
    2   0 LOAD_NAME    0 (__name__) 
       3 STORE_NAME    1 (__module__) 

    3   6 LOAD_NAME    2 (self) 
       9 LOAD_ATTR    3 (z) 
      12 LOAD_CONST    0 (<code object test ...>) 
      15 MAKE_FUNCTION   1 
      18 STORE_NAME    4 (test) 
      21 LOAD_LOCALS   
      22 RETURN_VALUE  

@uselpa:你的引擎收錄的例子(改寫2.X):

>>> code = compile(''' 
... default = 1 
... class Cl(object): 
...  def __init__(self, a=default): 
...   print a 
... Cl() 
... default = 2 
... Cl() 
... ''', '<input>', 'exec') 
>>> dis.dis(code) 
    2   0 LOAD_CONST    0 (1) 
       3 STORE_NAME    0 (default) 

    3   6 LOAD_CONST    1 ('Cl') 
       9 LOAD_NAME    1 (object) 
      12 BUILD_TUPLE    1 
      15 LOAD_CONST    2 (<code object Cl ...>) 
      18 MAKE_FUNCTION   0 
      21 CALL_FUNCTION   0 
      24 BUILD_CLASS   
      25 STORE_NAME    2 (Cl) 

    6   28 LOAD_NAME    2 (Cl) 
      31 CALL_FUNCTION   0 
      34 POP_TOP    

    7   35 LOAD_CONST    3 (2) 
      38 STORE_NAME    0 (default) 

    8   41 LOAD_NAME    2 (Cl) 
      44 CALL_FUNCTION   0 
      47 POP_TOP    
      48 LOAD_CONST    4 (None) 
      51 RETURN_VALUE   

正如你所看到的,類對象Cl(和函數對象__init__)只實例並存儲到本地名稱'Cl'一次。該模塊在運行時按順序執行,因此隨後重新命名名稱default將不會影響__init__中的默認值。

你可以使用以前編譯代碼和一個新的默認值動態實例化一個新的功能:

>>> default = 1 
>>> class Cl(object): 
...  def __init__(self, a=default): 
...   print a 
... 

>>> from types import FunctionType 
>>> default = 2 
>>> Cl.__init__ = FunctionType(
... Cl.__init__.__code__, globals(), '__init__', (default,), None) 
>>> c = Cl() 
2 

這從__init__.__code__重用已編譯的代碼對象來創建功能與新的__defaults__元組:

>>> Cl.__init__.__defaults__ 
(2,) 
+1

我被卡住了。如果類和方法是在實例化時創建的,那麼爲什麼它們顯然不在http://pastebin.com/UJfMpnqW上的示例中? – uselpa 2013-03-03 22:39:50

+0

@uselpa:我添加了你的例子,希望澄清「編譯時間」和「運行時間」之間的區別。模塊可以很好地編譯,沒有語法錯誤,但在導入(執行)時仍然存在運行時錯誤。 – eryksun 2013-03-04 04:18:09

+0

我想我明白了。非常感謝您的耐心等待! – uselpa 2013-03-04 06:15:23

相關問題