在类的内部使用类中属性时,类名.属性名和self.属性名的异同

本文聚焦Python中类和函数定义产生的命名空间。指出定义函数和类时都会形成命名空间,但存在差别。在公共和函数命名空间引用变量较简单,而类中引用属性有类名.属性名和self.属性名两种方式。还介绍了类方法与实例方法的绑定情况及类实例化次数记录等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在python中,定义了结构之后就产生了相应的命名空间,对类和函数的定义都会形成相应的命名空间。但是在两者的命名空间中有个差别值得我们关注,尤其在定义类中的函数时应该注意类命名空间的特性。

定义函数时产生的命名空间

定义一个简单函数:

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

调用这个函数:

In [12]: foo()
6

在我们定义的函数foo中,变量a,b,c,d都属于同一个命名空间,所以它们各自的作用域都会扩充到整个函数的命名空间,d可以直接访问a,b,c。

定义类时产生的命名空间

我们再来定义一个类:

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

我们对A实例化之后调用其中的foo函数:

In [14]: a = A()

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

<ipython-input-13-5eb5cb5b2468> in foo(self)
      4     c = 3
      5     def foo(self):
----> 6         d = a + b + c
      7         print(d)
      8

NameError: name 'b' is not defined

报错,明明a,b,c和foo都在同一个命名空间,但是在foo中不能解析变量a,b,c指向的对象(python中一切都是对象,程序中存储的所有数据都是对象)。和公共命名空间及函数定义形成的命名空间中的规则不同。

In [20]: a = 1

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

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

In [24]: foo()
1

上面的例子说明在公共命名空间和定义函数形成的命名空间中,引用处于同一命名空间的变量时只需要直接写出变量名就可以了。

那么我们应该怎么去引用类中的属性呢?有两种方式:类名.属性名和self.属性名

重新改写类A的定义:

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

a = A()

用a调用foo:

In [26]: a.foo()
3

用self.变量名引用类中的变量有个问题,就是这些变量和对象实例有关,而不是和类有关。看个例子:

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

A中的属性foo不是方法而是普通函数,我们需要通过类名来调用:

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

<ipython-input-26-185d314e2303> in foo()
      2     a = 1
      3     def foo():
----> 4         b = self.a
      5         print(b)
      6

NameError: name 'self' is not defined

因为没有对A实例化,所以self.变量名的方式行不通。

再来看个例子:

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

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

我们希望A的类方法countTheInitNum可以记录A的实例化对象数量。现在有a和b两个实例化对象,countTheInitNum应该输出2才对,我们来试试:

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

<ipython-input-28-e0179896d160> in countTheInitNum(cls)
      6     @classmethod
      7     def countTheInitNum(cls):
----> 8         print(self.count)
      9 a = A()
     10 b = A()

NameError: name 'self' is not defined

因为countTheInitNum是类方法,所以它是跟A绑定的,a.countTheInitNum就相当于A.countTheInitNum。

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

类方法是和类绑定的,类的实例方法是和实例绑定的。
我们改写一下A:

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

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

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

调用A的类方法countTheInitNum:

In [33]: A.countTheInitNum()
3

这次A的类方法countTheInitNum输出了正确的A类的实例化次数。

涉及到的几个概念
  • 命名空间
  • 类方法
  • 类的实例化
  • 绑定方法
  • 非绑定方法
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值