Python中的cls变量

技术背景

在Python的类型设计中,有时候会遇到一个cls参数。其实cls参数就是一个约定俗成的名称,用其他的名字也能正常运行但不建议这么用。它的作用类似于实例方法中的self参数,代表的是类本身,可以用于访问类的参数和类的方法。本文通过一些具体示例,来演示cls参数的功能和用法。

简单类实现

首先我们用普通的方法做一个最基本的测试案例:

class Test:
    def __init__(self):
        self.prefix = "Hello\t"

    def excute(self, x):
        print (self.prefix+x)

t = Test()
x = "Bob"
t.excute(x)
x = "Alice"
t.excute(x)

这里Test类型的操作逻辑是,在初始化函数中初始化一个prefix变量,然后在excute中调用打印函数,打印prefix变量和一个外部输入变量的整合字符串,执行效果如下:

Hello   Bob
Hello   Alice

这个方法的一个局限性在于,类Test中的函数,如excute函数,必须要新建一个实例t之后,才能够调用到它的excute方法。如果跳过初始化的步骤直接调用excute方法:

class Test:
    def __init__(self):
        self.prefix = "Hello\t"

    def excute(self, x):
        print (self.prefix+x)

Test.excute()

运行结果会告诉你,这需要两个变量的输入才能够正常的运行:

Traceback (most recent call last):
  File "/home/test_cls.py", line 8, in <module>
    Test.excute()
TypeError: Test.excute() missing 2 required positional arguments: 'self' and 'x'

例如,我们先初始化一个t实例,但是方法调用我们不调用t中的excute函数,而是直接调用Test类中的函数:

class Test:
    def __init__(self):
        self.prefix = "Hello\t"

    def excute(self, x):
        print (self.prefix+x)

t = Test()
x = "Bob"
Test.excute(t, x)

这样也是可以正常运行的:

Hello   Bob

classmethod方法

通过classmethod方法,可以允许我们不需要在外部对类初始化,而直接访问到类的内部属性、参数和函数。也就是对于classmethod装饰的函数,约定使用cls变量作为开头。

class Test:
    prefix = "Hello\t"
    @classmethod
    def excute(cls, x):
        print (cls.prefix+x)

x = "Bob"
Test.excute(x)

这样就可以直接在外部调用到类的内部函数:

Hello   Bob

当然,前面提到过,这里即使换一个变量名,也是可以正常运行的:

class Test:
    prefix = "Hello\t"
    @classmethod
    def excute(self, x):
        print (self.prefix+x)

x = "Bob"
Test.excute(x)

因为第一个参数代表的是类本身,因此可以执行成功:

Hello   Bob

这里需要说明的是,classmethod装饰器的作用,就是把函数的第一个参数相关的内容给省去了,如果不使用classmethod进行装饰,例如:

class Test:
    prefix = "Hello\t"
    def excute(cls, x):
        print (cls.prefix+x)

x = "Bob"
Test.excute(x)

这样运行会报错:

Traceback (most recent call last):
  File "/home/test_cls.py", line 7, in <module>
    Test.excute(x)
TypeError: Test.excute() missing 1 required positional argument: 'x'

提示的内容是参数缺失,其实也就是少了一个初始化的步骤。那么有一种情况是,类似于prefix这种的类属性是在__init__函数中定义的,这是比较常见的情况。在这种情况下,如果不初始化一个实例,就无法访问到初始化参数。但是前面也提到了,cls就代表类本身,那么自然可以通过cls来访问类中的函数,包括初始化的函数:

class Test:
    def __init__(self):
        self.prefix = "Hello\t"

    @classmethod
    def excute(cls, x):
        cls.__init__(cls)
        print (cls.prefix+x)

x = "Bob"
Test.excute(x)

这个代码可以被正确执行:

Hello   Bob

同时,通过classmethod,可以修改类的属性:

class Test:
    prefix = "Hello\t"
    @classmethod
    def excute(cls, x):
        print (cls.prefix+x)
        cls.prefix = cls.prefix+x+"\t"

x = "Bob"
Test.excute(x)
x = "Alice"
Test.excute(x)

这里在excute函数中,每次打印之后,都会修改一下prefix参数,所以打印输出结果如下:

Hello   Bob
Hello   Bob     Alice

当然,修改属性这样的操作,在普通的类实现中也是可以操作的:

class Test:
    def __init__(self):
        self.prefix = "Hello\t"

    def excute(self, x):
        print (self.prefix+x)
        self.prefix = self.prefix+x+"\t"

t = Test()
x = "Bob"
t.excute(x)
x = "Alice"
t.excute(x)

用self得到的结果是一样的:

Hello   Bob
Hello   Bob     Alice

如果不使用classmethod,也可以通过staticmethod来实现一个类似功能:

class Test:
    def __init__(self):
        self.prefix = "Hello\t"
    @staticmethod
    def excute(self, x):
        print (self.prefix+x)
        self.prefix = self.prefix+x+"\t"

t = Test()
x = "Bob"
Test.excute(t, x)
x = "Alice"
Test.excute(t, x)

但是staticmethod不对参数进行初始化,虽然可以在外部直接调用类函数,但是需要手动初始化一个实例。输出结果是一致的:

Hello   Bob
Hello   Bob     Alice

总结概要

本文介绍了在Python的classmethod装饰的类方法的cls变量的意义,通过几个不同的示例对比,凸显cls变量在Python编程中的应用场景。对于大多数的场景来说,使用普通的Python类和函数定义即可。如果需要在类的外部使用类的内部函数,但是可能有多个不同初始化的类输入,那么可以使用staticmethod进行装饰。如果只有一个类,而有多种不同的输入场景下,可以使用classmethod进行装饰。

版权声明

本文首发链接为:https://www.cnblogs.com/dechinphy/p/cls.html

作者ID:DechinPhy

更多原著文章:https://www.cnblogs.com/dechinphy/

请博主喝咖啡:https://www.cnblogs.com/dechinphy/gallery/image/379634.html

原创作者: dechinphy 转载于: https://www.cnblogs.com/dechinphy/p/18908706/cls
<think>嗯,用户问的是Python中的全局变量。首先,我得确认自己对全局变量的理解是否正确。全局变量是在模块级别定义的变量,在整个模块中都可以访问。不过在类的上下文中,全局变量可能指的是在类外部定义的变量,或者是在类内部所有方法中都能访问的变量。 接下来,用户可能想知道如何在类中使用这些全局变量。需要区分类的属性和全局变量。类的属性是属于类的变量,所有实例共享,而全局变量则是模块级别的。如果在类的方法内部想要修改全局变量,必须使用global关键字声明,这一点很重要,否则可能会在方法中创建一个同名的局部变量,导致错误。 可能用户遇到的问题是,在类的方法中修改了全局变量,但发现没有生效,这时候就需要检查是否遗漏了global声明。另外,有时候可能会混淆类属性和全局变量,比如直接在类中引用外部变量,这时候是否需要使用global,或者是否应该将变量作为类属性来定义。 还需要考虑类的继承中全局变量的使用,是否子类可以访问父类所在的模块中的全局变量。此外,全局变量的作用域问题,如果在不同的模块中导入类,全局变量是否仍然有效。 另外,可能用户想知道是否有替代方案,比如使用类属性或实例属性来代替全局变量,以提高代码的封装性和可维护性。这时候需要比较几种方法的优缺点,帮助用户做出合适的选择。 最后,应该通过示例代码来展示正确和错误的用法,让用户更直观地理解。例如,在类的方法中修改全局变量时使用global,以及在类外部定义全局变量,然后在类方法中访问和修改它。同时指出常见的错误,比如忘记声明global导致意外创建局部变量。 总结来说,需要分步骤解释全局变量在类中的使用方法,注意事项,常见错误及解决方案,并提供示例说明,确保用户能够清晰理解并正确应用。</think>在Python类中使用全局变量时,需要特别注意作用域和访问方式。以下是分步说明: 1. **全局变量定义** 全局变量必须定义在类外部: ```python global_var = 10 # 模块级全局变量 class MyClass: ... ``` 2. **类内访问全局变量** 在类方法中可直接读取全局变量: ```python class MyClass: def print_global(self): print(global_var) # 直接访问 ``` 3. **修改全局变量** 必须显式声明`global`: ```python class MyClass: def modify_global(self): global global_var # 必须声明 global_var += 1 ``` 4. **典型错误场景** 未声明global会导致创建局部变量: ```python class MyClass: def bad_modify(self): global_var = 20 # 创建了新的局部变量,不会修改全局变量 ``` 5. **替代方案建议** 推荐使用类属性代替全局变量: ```python class MyClass: class_var = 10 # 类属性 @classmethod def modify_class_var(cls): cls.class_var += 1 # 通过类方法修改 ``` 6. **全局变量与实例变量的区别** - 全局变量:模块级作用域 - 类属性:类级别共享 - 实例属性:每个实例独立 **最佳实践建议**: 1. 优先使用类属性替代全局变量 2. 必须修改全局变量时,严格使用`global`声明 3. 避免在多线程环境中修改全局变量 4. 全局变量命名建议使用全大写(如`GLOBAL_VAR`)提高可识别性 示例完整演示: ```python GLOBAL_COUNT = 0 class Counter: def increment(self): global GLOBAL_COUNT GLOBAL_COUNT += 1 def show(self): print(f"当前全局计数:{GLOBAL_COUNT}") c1 = Counter() c1.increment() # GLOBAL_COUNT = 1 c1.show() # 输出:当前全局计数:1 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值