Python3 特性(property)、静态方法(@staticmethod)和类方法(@classmethod)、__getattr__、__setattr__

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

目录

 

1. 特性

1.1 函数property

1.2 静态方法(@staticmethod)和类方法(@classmethod)

1.3 __getattr__、__setattr__等方法*


1. 特性

我们都知道在定义类时要定义用于获取或设置属性的方法(这些属性可能是私有的)。如果访问给定属性时必须采取特定的措施,那么像这样像这样封装状态变量(属性)就很重要。下面让我们看一下Rectangle类:

class Rectangle:
    def __init__(self):
        self.width = 0
        self.height = 0
    def set_size(self, size):
        self.width, self.height = size
    def get_size(self):
        return self.width, self.height

下面的实例演示了如何使用这个类:

>>> r = Rectangle()
>>> r.width = 10
>>> r.height = 5
>>> r.get_size()
(10, 5)
>>> r.set_size((150, 100))
>>> r.width
150

get_size和set_size是假想属性size的存取方法,这个属性是一个由width和height组成的元组。这些代码并非错误,但存在缺陷。使用这个类时,无需关心它是如何实现的(封装)。如果有一天你想修改实现,让size成为真正的属性,而width和height是动态计算出来的,就需要提供用于访问width和height的存取方法,使用这个类的程序也必须重写。应让客户端代码(使用你所编写代码的代码)能够以同样的方式对待所有的属性。

那么如何解决这个问题呢?给所有的属性都提供存取方法吗?这当然并非不可能,但如果有大量简单的属性,这样做就不现实(而且有点傻),因为将需要编写大量这样的存取方法,除了获取或设置属性外什么都不做。所幸Python能够替你隐藏存取方法,让所有的属性看起来都一样。通过存取方法定义的属性通常称为特性(property)

在Python中,实际上有两种创建特定的机制,我将重点介绍较新的那种——函数property,它只能用于新式类。随后,我将简单说明如何使用魔法方法来实现特性。

1.1 函数property

函数property使用起来很简单。如果你编写一个如上提及的Rectangle类。只需要再添加一行代码:

class Rectangle:
    def __init__ (self):
        self.width = 0
        self.height = 0
    def set_size(self, size):
        self.width, self.height = size
    def get_size(self):
        return self.width, self.height
    size = property(get_size, set_size)

在这个新版的Rectangle中,通过调用函数property并将存取方法作为参数(获取方法在前,设置方法在后)创建了一个特性,然后将名称size关联到这个特性。这样,你就能以同样的方式对待width、height和size,而无需关心它们是如何实现的。

>>> r = Rectangle()
>>> r.width = 10
>>> r.height = 5
>>> r.size
(10, 5)
>>> r.size = 150, 100
>>> r.width
150

如你所见,属性size依然受制于get_size和set_size执行的计算,但看起来就像普通属性一样。

实际上,调用函数property时,还可不指定参数、指定一个参数、指定三个参数或指定四个参数。如果没有指定任何参数,创建的特性将既不可读也不可写。如果只指定一个参数(获取方法),创建的特性将是只读的。第三个参数是可选的,指定用于删除属性的方法(这个方法不接受任何参数)。第四个参数也是可选的,指定一个文档字符串。这些参数分别名为fget、fset、fdel和doc。如果你要创建一个只可写且带文档字符串的特性,可使用它们作为关键字参数来实现。

对于新式类,应使用特性而不是存取方法。


1.2 静态方法(@staticmethod)和类方法(@classmethod)

讨论就得特性实现方式之前,先来说说另外两种方式类似于新式特性的功能。静态方法和类方法是这样创建的:将它们分别包装在staticmethod和classmethod类的对象中。静态方法的定义中没有参数self,可直接通过类来调用类方法的定义中包含类似于self的参数,通常被命名为cls。对于类方法,可直接通过类来调用,也可以通过对象直接调用,但参数cls将自动关联到类。下面是一个简单的示例:

class MyClass:
    def smeth():
        print('This is a static method')
        smeth = staticmethod(smeth)
    def cmeth(cls):
        print('This is a class method of', cls)
        cmeth = classmethod(cmeth)

像这样手工包装和替换方法有点繁琐。在Python 2.4中,引入了一种名为装饰器的新语法,可用于像这样包装方法。(实际上,装饰器可用于包装任何可调用的对象,并且可用于方法和函数。)可指定一个或多个装饰器,为此可在方法(或函数)前面使用运算符@列出这些装饰器(指定了多个装饰器时,应用的顺序与列出的顺序相反)。

class MyClass:
    @staticmethod
    def smeth():
        print('This is a static method')
    @classmethod
    def cmeth(cls):
        print('This is a class method of', cls)

定义这些方法后,就可像下面这样使用它们(无需实例化类):

>>> MyClass.smeth()
This is a static method
>>> MyClass.cmeth()
This is a class method of <class '__main__.MyClass'>

在Python中,静态方法和类方法以前一直都不太重要,主要是因为从某种程度上说,总是可以使用函数或关联的方法替代它们,而且早期的Python版本并不支持它们。因此,虽然较新的代码没有大量使用它们,但它们确实有用武之地(如工厂函数),因此你或许应该考虑使用它们

1.3 __getattr__、__setattr__等方法*

可以拦截对对象属性的所有访问企图,其用途之一是在旧类中实现特性(在旧式类中,函数property的行为不符合预期)。要在属性被访问时执行一段代码,必须使用一些魔法方法。下面的四个魔法方法提供了你需要的所有功能(在旧式类中,只需使用后面三个)。

  • __getattribute__(self, name): 在属性被访问时自动调用(只适用于新式类)。
  • __getattr__(self, name): 在属性被访问而对象没有这样的属性时自动调用。
  • __setattr__(self, name, value)__: 试图给属性赋值时自动调用。
  • __delattr__(self, name)__: 试图删除属性时自动调用。

相比于函数property,这些魔法方法使用起来要棘手些(从某种程度上说,效率更低),但他们更有用,以为内你可以在这些方法中编写处理多个特征的代码。然而,在可以选择的情况,还是优先使用函数property。

下面使用魔法方法试下前面的Rectangle示例:

class Rectangle:
    def __init__ (self):
        self.width = 0
        self.height = 0
    def __setattr__(self, name, value):
        if name == 'size':
            self.width, self.height = value
        else:
            self. __dict__[name] = value
    def __getattr__(self, name):
        if name == 'size':
            return self.width, self.height
        else:
            raise AttributeError()

如你所见,这个版本需要处理额外的管理细节。对于这个代码示例,需要注意如下两点。

  • 即便涉及的属性不是size,也将调用方法__setattr__。因此这个方法必须考虑如下两种情形:如果涉及的属性为size,就执行与以前一样的操作;否则就使用魔法属性__dict__.__dict__属性是一个字典,其中包含所有的实例属性。之所以使用它而不是执行常规属性赋值,是因为旨在避免再次调用__setattr__,进而导致无限循环。
  • 仅当没有找到指定的属性时,才会调用方法__getattr__。这意味如果指定的名称不适size,这个方法将引发AttributeError异常。这在要让类能够正确地支持hasattr和getattr等内置函数时很重要。如果指定的名称为size,就使用前一个实现中的表达式。

下面是对上述使用到__setattr__与__getattr__魔法方法的Rectangle类测试:

rec_a = Rectangle()
rec_a.size = (2,3)
rec_a.area = 6
print(rec_a.size)  # (2, 3)
print(rec_a.height) # 3
print(rec_a.area)   # 6

 

您可能感兴趣的与本文相关的镜像

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

<think>嗯,用户之前已经询问了Python中类的学习介绍,现在他们想深入了解类方法@classmethod)。我需要确保解释清楚类方法的概念、用途以及静态方法、实例方法的区别。 首先,用户可能已经知道实例方法,但需要明确类方法的定义。类方法使用@classmethod装饰器,第一个参数是cls,代表类本身。这点很重要,因为它允许在方法内部访问修改类属性,或者创建新的实例。 接下来,用户可能想知道类方法的应用场景。比如工厂方法创建实例,或者修改类级别的状态。例如,在之前的例子中,Cat类使用create_kitten作为类方法来生成小猫实例。我需要举更多的例子,比如日期处理中的替代构造函数,或者单例模式中的应用。 然后,用户可能会混淆类方法静态方法。需要指出静态方法使用@staticmethod,不需要cls或self参数,仅仅是逻辑上的归类。而类方法必须操作类相关的属性或方法。例如,BankAccount的validate_amount是静态方法,因为它不涉及类或实例的状态。 还要注意类方法在继承中的行为。当子类继承并调用类方法时,cls参数会自动指向子类,这可以用于创建灵活的子类实例。例如,父类的工厂方法返回cls()时,子类调用会创建子类实例,这对扩展功能很有用。 用户可能的疑问还包括何时使用类方法而不是实例方法。需要强调类方法用于不依赖特定实例但需要类本身的操作,比如创建实例的不同方式,或者维护类级别的配置。 最后,确保解释清晰,结构分明,使用代码示例帮助理解,并避免引入过多复杂概念,保持与用户当前知识水平相符。可能还需要提醒一些常见错误,比如错误地使用实例变量在类方法中,或者混淆clsself。</think>以下是关于Python类方法(@classmethod)的深度解析,包含核心特性应用场景: ```python # ======================== # 基础定义与核心特征 # ======================== class DateProcessor: format = "%Y-%m-%d" # 类属性 @classmethod def set_format(cls, new_format): """修改类属性""" cls.format = new_format # 通过cls访问类属性 @classmethod def from_iso(cls, iso_str): """替代构造函数:解析ISO格式字符串""" return cls(*map(int, iso_str.split(&#39;-&#39;))) # 返回新实例 def __init__(self, year, month, day): self.year = year self.month = month self.day = day ``` ### 核心特性解析: 1. **参数特征**: - 第一个参数必须是`cls`(约定名称),自动传入当前类对象 - 可访问类属性但无法直接访问实例属性 2. **调用方式**: ```python # 类直接调用 DateProcessor.set_format("%d/%m/%Y") # 实例调用(不推荐) dp = DateProcessor(2023, 10, 1) dp.set_format("%m-%d-%Y") # 仍会修改类属性 ``` 3. **典型应用场景**: - **工厂模式**:创建预配置对象 ```python class DatabaseConnection: def __init__(self, config): self.config = config @classmethod def development_config(cls): return cls({"host": "localhost", "port": 5432}) ``` - **状态管理**:维护类级别状态 ```python class Counter: _count = 0 @classmethod def increment(cls): cls._count += 1 return cls._count ``` ### 与静态方法对比: ```python class TextUtils: @classmethod def normalize(cls, text): """依赖类属性的处理""" return text.lower() + cls._get_suffix() @staticmethod def _get_suffix(): """静态辅助方法""" return "!" @staticmethod def count_words(text): """独立工具方法""" return len(text.split()) ``` | 特性 | 类方法 | 静态方法 | |---------------------|--------------------------|-----------------------| | 参数 | 必须包含cls | 无强制参数 | | 类属性访问 | 可直接访问 | 需通过类名访问 | | 继承行为 | 自动获取子类引用 | 保持定义时类 | | 典型用途 | 工厂方法/类状态管理 | 工具函数/辅助方法 | ### 高级应用场景: **1. 多态构造器(继承场景)** ```python class Shape: @classmethod def from_json(cls, json_data): return cls(**json_data) # 子类调用时自动返回子类实例 class Circle(Shape): def __init__(self, radius): self.radius = radius # 使用父类方法创建子类实例 circle = Circle.from_json({"radius": 5}) ``` **2. 单例模式实现** ```python class Singleton: _instance = None def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance @classmethod def get_instance(cls): return cls() # 统一获取实例的入口 ``` **3. 类装饰器** ```python class debug_methods: def __init__(self, methods): self.methods = methods @classmethod def decorate(cls, *methods): def wrapper(the_class): for method in methods: original = getattr(the_class, method) setattr(the_class, method, cls._debug_wrap(original)) return the_class return wrapper @classmethod def _debug_wrap(cls, func): def wrapped(*args, **kwargs): print(f"Calling {func.__name__}") return func(*args, **kwargs) return wrapped @debug_methods.decorate(&#39;login&#39;, &#39;logout&#39;) class UserSystem: def login(self): ... def logout(self): ... ``` ### 最佳实践建议: 1. **命名规范**:使用`cls`作为第一个参数,区别于实例方法的`self` 2. **继承安全**:使用`cls()`代替硬编码类名创建实例 3. **状态隔离**:避免在类方法中修改全局状态 4. **职责划分**:当方法既不需要实例也不需要类时,优先选择静态方法 理解类方法的本质(绑定到类而非实例的操作),能够帮助开发者更优雅地组织代码结构,实现灵活的类级别操作继承扩展。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值