title: python的特别方法与装饰器
author: Sekyoro
date: 2023-05-07 11:31:24
tags:

- python

categories:
archives:
password:
abstract:

message:

这里将介绍python的所谓魔法方法以及装饰器
<!--more-->

魔术方法

一般在类中以双下划线包围的方法就是魔术方法,或者叫特殊方法。

简单来说,Python的魔术方法是为了利用Python的标准方法以及不用去记住标准操作的名称,实现更统一的接口。

image-20230507133421451

例如下面的代码

import collections

Card = collections.namedtuple('Card', ['rank', 'suit'])

class FrenchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()

    def __init__(self):
        self._cards = [Card(rank, suit) for suit in self.suits
                                        for rank in self.ranks]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, item):
        return self._cards[item]


if __name__ == '__main__':
    print(len(deck))
    print(deck[0])

__len__这个特别方法使得调用len(beer_card)实际调用__len__

getitem 方法把 [] 操作交给了 self._cards 列表,所以我们的 deck 类自动支持切 片(slicing)操作。

输出如下

52
Card(rank='2', suit='spades')

另外,仅仅实现了 getitem 方法,对象就变成可迭代的了

for i in deck:
    print(i)

image-20230507134341143

同时__contains__可以实现in方法

除了上面通过特殊方法实现迭代,len(),切片等方法外,还可以实现类似重载运算符的效果。

from math import hypot 
class Vector:

    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __repr__(self):
        return 'Vector(%r, %r)' % (self.x, self.y)

    def __abs__(self):
        return hypot(self.x, self.y)

    def __bool__(self):
        return bool(abs(self))

    def __add__(self, other):
        x = self.x + other.x
        y = self.y + other.y
        return Vector(x, y)

    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)
v1 = Vector(3,4)
v2 = Vector(4,5)
v3 = v1+v2
print(v3)

输出Vector(7, 9)

还有__repr____str__

reprstr 的区别在于,后者是在 str() 函数被使用,或是在用 print 函数打印一个对象的时候才被调用的,并且它返回的字符串对终端用户更友好。

如果你只想实现这两个特殊方法中的一个,__repr__ 是更好的选择,因为如果一个对象没有 str 函数,而 Python 又需要调用它的时候,解释器会用 repr 作为替代。

  • {!r}就是使用format语法时候的%r。因此,我们只需要关注%r就好。
  • %r表示的用repr()处理;类似于的%s表示用str()处理一样

其他特别方法

image-20230507134927018

装饰器

装饰器的作用就是为已经存在的函数或对象添加额外的功能。
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
def debug(func):
    def wrapper():
        print("[DEBUG]: enter {}()".format(func.__name__))
        return func()
    return wrapper

@debug
def hello():
    print("hello")

hello()

装饰器涉及到闭包的概念,什么是闭包,一句话说就是,在函数中再嵌套一个函数,并且引用外部函数的变量,这就是一个闭包了

上述无参装饰器可以用于输出日志。

如果要在wrapper中访问参数,如下

def debug(func):
    def wrapper(*args, **kwargs):
        print("[DEBUG]: enter {}()".format(func.__name__))
        print("params", args, kwargs)
        return func(*args, **kwargs)

    return wrapper


@debug
def test(a,b):
    print("this is a test function")

如果要在装饰器中使用参数,还要在外面包围一层。

def logging(level):
    def outwrapper(func):
        def wrapper(*args, **kwargs):
            print("[{0}]: enter {1}()".format(level, func.__name__))
            return func(*args, **kwargs)

        return wrapper

    return outwrapper


@logging(level="INFO")
def hello(a, b, c):
    print(a, b, c)

除了使用函数装饰器也可以是使用类装饰器

class logging(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("[DEBUG]: enter {}()".format(self.func.__name__))
        return self.func(*args, **kwargs)

@logging
def hello(a, b, c):
    print(a, b, c)

装饰器中使用参数

class logging(object):
    def __init__(self, level):
        self.level = level

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print("[{0}]: enter {1}()".format(self.level, func.__name__))
            return func(*args, **kwargs)
        return wrapper

@logging(level="TEST")
def hello(a, b, c):
    print(a, b, c)

属性

当我们创建一个类,以定义对象时,我们可能会希望一些属性对于外界是只读的,或者希望创建属性只能按特定的方式访问或修改。这时就可以使用 Python 中的属性(Property)。

在 Python 中,属性是包装在函数中的代码段,它们能够拦截对对象属性的访问和修改。Python 内置了@property 装饰器,可以用来修饰一个方法,使之成为属性调用。同时,Python 还提供了 @property 修饰器的对应方法的 setter 方法,用于设置属性。

简单来说,属性优化了之前使用公共方法来访问私有属性得方法同时有更精细的粒度。

class myObj:

    def __init__(self, name):
        self.__name = name
        print("init")

    @property
    def name(self):
        return self.__name

if __name__ == '__main__':
    obj = myObj("hello")
    print(obj.name)

输出

init
hello

如果使用obj.name = "world"更改属性则会报错.

添加

@name.setter
def name(self, newname):
     self.__name = newname

可以通过name属性更改self.__name

此外还有deleter

@name.deleter
def name(self):
     self.__name = None

使用del删除属性

del obj.name
print(obj.name)

输出就是None

参考资料

  1. Python常用魔术方法 - 知乎 (zhihu.com)
  2. 《流畅的Python》
  3. Python基础(十四)—装饰器 wrapper_luoz_python的博客-CSDN博客
  4. python 装饰器详解 - 知乎 (zhihu.com)
  5. Python @property属性详解 - 知乎 (zhihu.com)

本文由mdnice多平台发布


长亭
1 声望0 粉丝