3.3 python关于元类及property装饰器的使用

1、元类的使用

import inspect


class B(type):

    def __new__(mcs, name, bases, attrs):
        """
        @:param msc 元类自身
        @:param name 类名,不是元类的name
        @:param bases 继承父类的元组集合,不是元类的父类
        @:param attrs 类的属性字典集合
        """

        for attr_name, attr_value in attrs.items():

            if attr_name == 'say' and callable(attr_value):
                # 通过inspect可以获取,类方法中的参数
                print(inspect.signature(attr_value).parameters)
        return super().__new__(mcs, name, bases, attrs)


class C:
    pass


class A(C, metaclass=B):

    def say(self, name):
        print(name)

2、@property

class D:

    def __init__(self, name):
        self._name = name

    @property
    def g(self):
        return self._name

    # 必须以被property标记的方法开始
    @g.setter
    def g(self, v):
        self._name = v


d = D('hell')
print(d.g)
d.g = 222
print(d.g)

<think>我们正在讨论Python中的抽象基(ABC)模块,具体是关于抽象方法、抽象方法和抽象静态方法的区别。这些概念在`abc`模块中实现,用于定义抽象基,要求子必须实现特定的方法。根据引用的内容,我们可以总结以下几点:1.**抽象方法**:在基中声明但没有实现的方法,子必须提供实现。在Python中,使用`@abstractmethod`装饰器标记。它可以是普通实例方法。2.**抽象方法**:使用`@classmethod`和`@abstractmethod`装饰器,要求子实现一个方法。方法的第一个参数是`cls`,代表本身。3.**抽象静态方法**:使用`@staticmethod`和`@abstractmethod`装饰器,要求子实现一个静态方法。静态方法没有特殊的第一个参数(如`self`或`cls`)。需要注意的是,装饰器的顺序很重要:`@abstractmethod`应该是最内层的装饰器(即最靠近函数定义的那个),然后才是`@classmethod`或`@staticmethod`。但在实际使用中,由于`@abstractmethod`对函数描述符的处理,正确的顺序是先使用`@classmethod`或`@staticmethod`,然后再用`@abstractmethod`(即`@abstractmethod`在更外层)。然而,在引用[3]中给出了一个例子,其中`@classmethod`在下面,而`@abstractmethod`在上面,这实际上也是可行的,因为Python3允许这样的顺序。但通常为了清晰,推荐将`@abstractmethod`放在最上面(即更靠近函数定义的位置)?根据引用[1]中的描述:“@abstractmethod要紧挨着函数定义”,所以应该是`@abstractmethod`在装饰器堆叠的最里面,也就是最靠近函数定义的那个。然而,在引用[3]的例子中,我们看到:```@classmethod@abc.abstractmethoddefget_ingredients(cls):```这里`@abc.abstractmethod`在`@classmethod`的上面(即离函数定义更近)。实际上,根据Python官方文档和实际使用,正确的顺序应该是:`@abstractmethod`应该放在最里面,即最靠近函数定义的位置。但是,对于方法和静态方法,由于它们本身就是装饰器,所以通常我们会这样写:-对于抽象方法:```python@abc.abstractmethod@classmethoddef...(cls):...```然而,引用[1]和引用[3]的例子都显示`@abstractmethod`在`@classmethod`的上面(即更靠近函数)。这实际上是因为`@classmethod`和`@staticmethod`装饰器会改变函数为描述符,而`@abstractmethod`需要正确地标记这个描述符。所以正确的顺序是:先使用`@classmethod`或`@staticmethod`,然后再用`@abstractmethod`(即`@abstractmethod`在更外层)。但引用[1]中的例子是:```python@staticmethod@abc.abstractmethoddefcompare_animals(animal1,animal2):```以及```python@classmethod@abc.abstractmethoddefget_species(cls):```都是将`@abstractmethod`放在`@classmethod`或`@staticmethod`的里面(更靠近函数)。这实际上是一个常见的混淆点。根据[官方文档](https://docs.python.org/3/library/abc.html)中的说明,抽象方法可以与其他方法装饰器一起使用,但必须注意顺序:`@abstractmethod`应该放在最里面(即最靠近函数定义)。但是,在Python3.3+中,顺序实际上可以反过来(即`@abstractmethod`在外面)也能工作。然而,为了兼容性和清晰,推荐将`@abstractmethod`放在最里面(即最靠近函数定义),然后才是其他装饰器(如`@classmethod`、`@staticmethod`)在外面。然而,我们来看一个官方例子(来自Python3.10文档):```pythonclassC(ABC):@abstractmethoddefmy_abstract_method(self,...):...@classmethod@abstractmethoddefmy_abstract_classmethod(cls,...):...@staticmethod@abstractmethoddefmy_abstract_staticmethod(...):...@property@abstractmethoddefmy_abstract_property(self):...@my_abstract_property.setter@abstractmethoddefmy_abstract_property(self,val):...@abstractmethoddef_get_x(self):...@abstractmethoddef_set_x(self,val):...x=property(_get_x,_set_x)```这里对于方法和静态方法,都是将`@classmethod`或`@staticmethod`放在`@abstractmethod`的上面(即更靠近函数),而`@abstractmethod`在更外层。这实际上是官方推荐的写法。因此,结论是:为了正确标记抽象方法,当与`@classmethod`、`@staticmethod`等结合时,`@abstractmethod`应紧接在函数定义之上,然后上面再放其他装饰器。但实际上,按照官方文档,我们看到的是其他装饰器在`@abstractmethod`之上。所以,我们应该按照:其他装饰器(如`@classmethod`)先应用,然后再用`@abstractmethod`。即:@classmethod@abstractmethoddef...(cls):...但注意,官方文档中的例子确实是这样写的。现在,我们讨论它们的区别:###抽象方法(普通抽象方法)-是一个实例方法,第一个参数是`self`。-子必须实现一个同名的实例方法。-调用时,通过实例调用。###抽象方法-使用`@classmethod`装饰,同时标记为`@abstractmethod`。-第一个参数是`cls`,代表本身。-子必须实现一个同名的方法。-调用时,可以通过或实例调用(但通常使用调用)。###抽象静态方法-使用`@staticmethod`装饰,同时标记为`@abstractmethod`。-没有必须的特殊参数(如`self`或`cls`)。-子必须实现一个同名的静态方法。-调用时,可以通过或实例调用。###关键区别1.**参数传递**:抽象方法的第一个参数是实例(`self`),抽象方法的第一个参数是(`cls`),抽象静态方法则没有自动传递的参数。2.**调用方式**:抽象方法通常通过实例调用;抽象方法可以通过或实例调用,但通常用调用;抽象静态方法也可以通过或实例调用。3.**使用场景**:-抽象方法:需要访问实例状态时使用。-抽象方法:需要访问状态(如属性)或创建相关的工厂方法时使用。-抽象静态方法:与相关但不需要访问或实例状态时使用。###示例代码```pythonimportabcclassAbstractExample(abc.ABC):#抽象方法(实例方法)@abc.abstractmethoddefregular_method(self):pass#抽象方法@classmethod@abc.abstractmethoddefclass_method(cls):pass#抽象静态方法@staticmethod@abc.abstractmethoddefstatic_method():passclassConcreteExample(AbstractExample):defregular_method(self):print("Regularmethodimplementation")@classmethoddefclass_method(cls):print("Classmethodimplementation")@staticmethoddefstatic_method():print("Staticmethodimplementation")#使用obj=ConcreteExample()obj.regular_method()#调用抽象实例方法ConcreteExample.class_method()#调用抽象方法ConcreteExample.static_method()#调用抽象静态方法```###注意事项1.抽象基必须将其设置为`abc.ABCMeta`(通过`metaclass=abc.ABCMeta`或在Python3中直接继承`abc.ABC`)。2.如果子没有实现所有抽象方法,它将无法实例化,会抛出`TypeError`。###引用出处根据引用[1][2][3]中的描述,我们了解到这些抽象方法的装饰器用法以及它们的区别。特别是引用[1]展示了抽象静态方法和抽象方法的用法,引用[2]则说明了抽象方法的基本概念(似于Java的接口方法),引用[3]则说明了在抽象方法上使用方法和静态方法是可行的(在Python3中)。[^1][^2][^3]§§相关问题§§1.Python中,抽象基(ABC)的作用是什么?2.抽象与接口在Python中有何异同?3.如何强制子实现一个抽象属性?</think> ### Python抽象方法、抽象方法与抽象静态方法的区别 在Python中,抽象方法、抽象方法和抽象静态方法都是通过`abc`模块实现的抽象基(ABC)概念,但它们在使用场景和行为上有重要区别: #### 1. **抽象方法** - **定义**:使用`@abstractmethod`装饰的普通实例方法 - **特征**: - 必须通过实例调用 - 第一个参数是`self`(实例引用) - 子**必须实现具体逻辑** - **典型场景**:需要访问实例状态的操作 - **示例**: ```python from abc import ABC, abstractmethod class Animal(ABC): @abstractmethod def move(self): # 标准抽象方法 pass ``` #### 2. **抽象方法** - **定义**:结合`@classmethod`和`@abstractmethod` - **特征**: - 可通过名直接调用(无需实例化) - 第一个参数是`cls`(引用) - 子必须实现级别逻辑 - **典型场景**:工厂、状态操作或继承层次相关逻辑 - **示例**: ```python class Animal(ABC): @classmethod @abstractmethod # 装饰器顺序:classmethod在abstractmethod下方 def create_species(cls): # 抽象方法 pass ``` #### 3. **抽象静态方法 - **定义**:结合`@staticmethod`和`@abstractmethod` - **特征**: - 无隐含参数(无`self`或`cls`) - 通过或实例均可调用 - 子必须实现独立逻辑 - **典型场景**:纯工具函数(与/实例状态无关) - **示例**: ```python class Animal(ABC): @staticmethod @abstractmethod # 装饰器顺序相同 def compare(a, b): # 抽象静态方法 pass ``` ### 关键区别总结 | **特性** | 抽象方法 | 抽象方法 | 抽象静态方法 | |------------------|------------------|------------------|------------------| | **装饰器** | `@abstractmethod`| `@classmethod`<br>`@abstractmethod` | `@staticmethod`<br>`@abstractmethod` | | **首参数** | `self` (实例) | `cls` () | 无 | | **调用方式** | 实例调用 | /实例调用 | /实例调用 | | **访问权限** | 实例属性/方法 | 属性/方法 | 无特殊访问权 | | **典型用途** | 对象行为 | 工厂/操作 | 工具函数 | ### 注意事项 1. **装饰器顺序**:当组合装饰器时,必须将`@abstractmethod`放在最靠近函数的位置 ```python # ✅ 正确顺序 @classmethod @abstractmethod def my_method(cls): ... # ❌ 错误顺序(引发异常) @abstractmethod @classmethod def my_method(cls): ... ``` 2. **抽象基**:必须显式声明: ```python class Animal(metaclass=ABCmeta): ... # Python 3语法 ``` 3. **实现强制**:子若未实现任何抽象方法,实例化时将抛出`TypeError`[^1][^3] 4. **与普通方法对比**:抽象方法更侧重接口约束(Java接口风格),而普通Python方法可通过`raise NotImplementedError`模拟(但无编译时检查)[^2] ### 使用场景对比 ```python class Database(ABC): # 抽象方法 (需访问实例状态) @abstractmethod def connect(self): ... # 抽象方法 (级别操作) @classmethod @abstractmethod def get_driver_version(cls): ... # 抽象静态方法 (独立工具函数) @staticmethod @abstractmethod def sql_to_json(sql): ... ``` > ℹ️ 从Python 3.3+开始,抽象基可直接继承`abc.ABC`简化声明: > ```python > from abc import ABC, abstractmethod > > class MyBase(ABC): # 无需显式metaclass > ... > ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值