天天看點

在類的内部使用類中屬性時,類名.屬性名和self.屬性名的異同

在python中,定義了結構之後就産生了相應的命名空間,對類和函數的定義都會形成相應的命名空間。但是在兩者的命名空間中有個差别值得我們關注,尤其在定義類中的函數時應該注意類命名空間的特性。

定義函數時産生的命名空間

定義一個簡單函數:

def foo():
    a = 
    b = 
    c = 
    d = a + b + c
    print(d)
           

調用這個函數:

In []: foo()

           

在我們定義的函數foo中,變量a,b,c,d都屬于同一個命名空間,是以它們各自的作用域都會擴充到整個函數的命名空間,d可以直接通路a,b,c。

定義類時産生的命名空間

我們再來定義一個類:

class A:
    a = 
    b = 
    c = 
    def foo(self):
        d = a + b + c
        print(d)
           

我們對A執行個體化之後調用其中的foo函數:

In []: a = A()

In []: a.foo()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input--ae930c5e3b4a> in <module>()
---->  a.foo()

<ipython-input--eb5cb5b2468> in foo(self)
           c = 
           def foo(self):
---->          d = a + b + c
               print(d)
      

NameError: name 'b' is not defined
           

報錯,明明a,b,c和foo都在同一個命名空間,但是在foo中不能解析變量a,b,c指向的對象(python中一切都是對象,程式中存儲的所有資料都是對象)。和公共命名空間及函數定義形成的命名空間中的規則不同。

In []: a = 

In []: def foo():
    ...:     b = 
    ...:     print(b)

In []: foo()

           
In []: def foo():
    ...:     a = 
    ...:     def inner():
    ...:         b = a
    ...:         print(b)
    ...:     return inner()

In []: foo()

           

上面的例子說明在公共命名空間和定義函數形成的命名空間中,引用處于同一命名空間的變量時隻需要直接寫出變量名就可以了。

那麼我們應該怎麼去引用類中的屬性呢?有兩種方式:類名.屬性名和self.屬性名

重新改寫類A的定義:

class A:
    a = 
    b = 
    def foo(self):
        c = self.a + self.b
        print(c)

a = A()
           

用a調用foo:

In []: a.foo()

           

用self.變量名引用類中的變量有個問題,就是這些變量和對象執行個體有關,而不是和類有關。看個例子:

class A:
    a = 
    def foo():
        b = self.a
        print(b)
           

A中的屬性foo不是方法而是普通函數,我們需要通過類名來調用:

In []: A.foo()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input--cb0580e7a09> in <module>()
---->  A.foo()

<ipython-input--d314e2303> in foo()
           a = 
           def foo():
---->          b = self.a
               print(b)
      

NameError: name 'self' is not defined
           

因為沒有對A執行個體化,是以self.變量名的方式行不通。

再來看個例子:

class A:
    count = 
    def __init__(self):
        self.count += 

    @classmethod
    def countTheInitNum(cls):
        print(self.count)
a = A()
b = A()
           

我們希望A的類方法countTheInitNum可以記錄A的執行個體化對象數量。現在有a和b兩個執行個體化對象,countTheInitNum應該輸出2才對,我們來試試:

In []: a.countTheInitNum()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input--ab33913b354f> in <module>()
---->  a.countTheInitNum()

<ipython-input--e0179896d160> in countTheInitNum(cls)
           @classmethod
           def countTheInitNum(cls):
---->          print(self.count)
       a = A()
      b = A()

NameError: name 'self' is not defined
           

因為countTheInitNum是類方法,是以它是跟A綁定的,a.countTheInitNum就相當于A.countTheInitNum。

In []: A.countTheInitNum
Out[]: <bound method A.countTheInitNum of <class '__main__.A'>>
           

類方法是和類綁定的,類的執行個體方法是和執行個體綁定的。

我們改寫一下A:

class A:
    count = 
    def __init__(self):
        A.count += 

    @classmethod
    def countTheInitNum(cls):
        print(A.count)

a = A()
b = A()
c = A()
           

調用A的類方法countTheInitNum:

In []: A.countTheInitNum()

           

這次A的類方法countTheInitNum輸出了正确的A類的執行個體化次數。

涉及到的幾個概念

  • 命名空間
  • 類方法
  • 類的執行個體化
  • 綁定方法
  • 非綁定方法