python@装饰器 魔法方法详解

发布于:2024-10-08 ⋅ 阅读:(178) ⋅ 点赞:(0)

new(cls[, …]) 1. new 是在一个对象实例化的时候所调用的第一个方法
2. 它的第一个参数是这个类,其他的参数是用来直接传递给 init 方法
3. new 决定是否要使用该 init 方法,因为 new 可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果 new 没有返回实例对象,则 init 不会被调用
4. new 主要是用于继承一个不可变的类型比如一个 tuple 或者 string
init(self[, …]) 构造器,当一个实例被创建的时候调用的初始化方法
del(self) 析构器,当一个实例被销毁的时候调用的方法
call(self[, args…]) 允许一个类的实例像函数一样被调用:x(a, b) 调用 x.call(a, b)
len(self) 定义当被 len() 调用时的行为
repr(self) 定义当被 repr() 调用时的行为
str(self) 定义当被 str() 调用时的行为
bytes(self) 定义当被 bytes() 调用时的行为
hash(self) 定义当被 hash() 调用时的行为
bool(self) 定义当被 bool() 调用时的行为,应该返回 True 或 False
format(self, format_spec) 定义当被 format() 调用时的行为
有关属性
getattr(self, name) 定义当用户试图获取一个不存在的属性时的行为
getattribute(self, name) 定义当该类的属性被访问时的行为
setattr(self, name, value) 定义当一个属性被设置时的行为
delattr(self, name) 定义当一个属性被删除时的行为
dir(self) 定义当 dir() 被调用时的行为
get(self, instance, owner) 定义当描述符的值被取得时的行为
set(self, instance, value) 定义当描述符的值被改变时的行为
delete(self, instance) 定义当描述符的值被删除时的行为
比较操作符
lt(self, other) 定义小于号的行为:x < y 调用 x.lt(y)
le(self, other) 定义小于等于号的行为:x <= y 调用 x.le(y)
eq(self, other) 定义等于号的行为:x == y 调用 x.eq(y)
ne(self, other) 定义不等号的行为:x != y 调用 x.ne(y)
gt(self, other) 定义大于号的行为:x > y 调用 x.gt(y)
ge(self, other) 定义大于等于号的行为:x >= y 调用 x.ge(y)
算数运算符
add(self, other) 定义加法的行为:+
sub(self, other) 定义减法的行为:-
mul(self, other) 定义乘法的行为:*
truediv(self, other) 定义真除法的行为:/
floordiv(self, other) 定义整数除法的行为://
mod(self, other) 定义取模算法的行为:%
divmod(self, other) 定义当被 divmod() 调用时的行为
pow(self, other[, modulo]) 定义当被 power() 调用或 ** 运算时的行为
lshift(self, other) 定义按位左移位的行为:<<
rshift(self, other) 定义按位右移位的行为:>>
and(self, other) 定义按位与操作的行为:&
xor(self, other) 定义按位异或操作的行为:^
or(self, other) 定义按位或操作的行为:|
反运算
radd(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
rsub(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
rmul(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
rtruediv(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
rfloordiv(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
rmod(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
rdivmod(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
rpow(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
rlshift(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
rrshift(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
rxor(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
ror(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
增量赋值运算
iadd(self, other) 定义赋值加法的行为:+=
isub(self, other) 定义赋值减法的行为:-=
imul(self, other) 定义赋值乘法的行为:*=
itruediv(self, other) 定义赋值真除法的行为:/=
ifloordiv(self, other) 定义赋值整数除法的行为://=
imod(self, other) 定义赋值取模算法的行为:%=
ipow(self, other[, modulo]) 定义赋值幂运算的行为:**=
ilshift(self, other) 定义赋值按位左移位的行为:<<=
irshift(self, other) 定义赋值按位右移位的行为:>>=
iand(self, other) 定义赋值按位与操作的行为:&=
ixor(self, other) 定义赋值按位异或操作的行为:^=
ior(self, other) 定义赋值按位或操作的行为:|=
一元操作符
neg(self) 定义正号的行为:+x
pos(self) 定义负号的行为:-x
abs(self) 定义当被 abs() 调用时的行为
invert(self) 定义按位求反的行为:~x
类型转换
complex(self) 定义当被 complex() 调用时的行为(需要返回恰当的值)
int(self) 定义当被 int() 调用时的行为(需要返回恰当的值)
float(self) 定义当被 float() 调用时的行为(需要返回恰当的值)
round(self[, n]) 定义当被 round() 调用时的行为(需要返回恰当的值)
index(self) 1. 当对象是被应用在切片表达式中时,实现整形强制转换
2. 如果你定义了一个可能在切片时用到的定制的数值型,你应该定义 index
3. 如果 index 被定义,则 int 也需要被定义,且返回相同的值
上下文管理(with 语句)
enter(self) 1. 定义当使用 with 语句时的初始化行为
2. enter 的返回值被 with 语句的目标或者 as 后的名字绑定
exit(self, exc_type, exc_value, traceback) 1. 定义当一个代码块被执行或者终止后上下文管理器应该做什么
2. 一般被用来处理异常,清除工作或者做一些代码块执行完毕之后的日常工作
容器类型
len(self) 定义当被 len() 调用时的行为(返回容器中元素的个数)
getitem(self, key) 定义获取容器中指定元素的行为,相当于 self[key]
setitem(self, key, value) 定义设置容器中指定元素的行为,相当于 self[key] = value
delitem(self, key) 定义删除容器中指定元素的行为,相当于 del self[key]
iter(self) 定义当迭代容器中的元素的行为
reversed(self) 定义当被 reversed() 调用时的行为
contains(self, item) 定义当使用成员测试运算符(in 或 not in)时的行为

装饰器的基本结构如下

def decorator_function(original_function):
    def wrapper_function():
        # 额外的功能或操作
        print("Something is happening before the function is called.")
        original_function()  # 调用原始函数
        print("Something is happening after the function is called.")
    return wrapper_function
@decorator_function
def my_function():
    print("The function is called.")

my_function()



内置的装饰器,例如 @staticmethod、@classmethod 和 @property

@classmethod:定义类方法,该方法接收类作为第一个参数,而不是实例

class MyClass:
    class_variable = 0

    @classmethod
    def class_method(cls):
        print("This is a class method.")
        print(f"Class variable: {cls.class_variable}")

@property:将方法转换为只读属性

class MyClass: def init(self, value): self._value = value @property def value(self): return self._value @value.setter def value(self, new_value): self._value = new_value

自定义装饰器

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Something is happening before the function is called.")
        result = func(*args, **kwargs)  # 调用原始函数
        print("Something is happening after the function is called.")
        return result
    return wrapper

@my_decorator
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("Alice")


带参数的装饰器

def repeat(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                func(*args, **kwargs)
        return wrapper
    return decorator

@repeat(times=3)
def greet(name):
    print(f"Hello, {name}!")

greet("Bob")


装饰器可以进行链式调用

def decorator1(func):
    def wrapper(*args, **kwargs):
        print("Decorator 1")
        return func(*args, **kwargs)
    return wrapper

def decorator2(func):
    def wrapper(*args, **kwargs):
        print("Decorator 2")
        return func(*args, **kwargs)
    return wrapper

@decorator1
@decorator2
def my_function():
    print("Function is called.")

my_function()

装饰顺序从内到外,即 decorator2 包裹 decorator1

装饰器与带参数的函数

def log_function_call(func):
    def wrapper(*args, **kwargs):
        print(f"Calling function {func.__name__} with arguments {args} and keyword arguments {kwargs}")
        return func(*args, **kwargs)
    return wrapper

@log_function_call
def add(x, y):
    return x + y

result = add(5, 10)


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def display(self):
        print(f"Name: {self.name}, Age: {self.age}")

    @classmethod
    def from_string(cls, string):
        name, age = string.split(",")
        return cls(name, int(age))

person_str = "John,25"
person = Person.from_string(person_str)
person.display()  # 输出:Name: John, Age: 25

根据不同的类型参数返回不同的子类对象

class Shape:
    def __init__(self, color):
        self.color = color

    def display_color(self):
        print(f"Color: {self.color}")

    @classmethod
    def create_shape(cls, color, type):
        if type == "circle":
            return Circle(color)
        elif type == "rectangle":
            return Rectangle(color)

class Circle(Shape):
    def display_shape(self):
        print("Type: Circle")

class Rectangle(Shape):
    def display_shape(self):
        print("Type: Rectangle")

circle = Shape.create_shape("red", "circle")
circle.display_color()  # 输出:Color: red
circle.display_shape()  # 输出:Type: Circle

rectangle = Shape.create_shape("blue", "rectangle")
rectangle.display_color()  # 输出:Color: blue
rectangle.display_shape()  # 输出:Type: Rectangle

类方法中不能调用实例方法(第一个参数是self的方法)。但可以调用其他类方法或者静态方法(@staticmethod)

class MyClass:
    @classmethod
    def class_method(cls):
        print("This is a class method")
        cls.other_method()
        cls.other_method2()
        
    @staticmethod
    def other_method():
        print("This is another method")
        
    def instance_method(self):
        print("This is an instance method")    
    
    @classmethod
    def other_method2(cls):
        print("This is another method")
        obj = cls()
        obj.instance_method()

# 调用类方法
MyClass.class_method()

@property、@setter装饰器基本用法
定义
@property装饰器是Python中一个特殊的装饰器,用于定义类的属性。它提供了一种简洁的方式来定义属性的访问方法,并且可以在需要时进行计算或验证。应用于类的实例方法,将其转换为类的属性。通过使用@property装饰器,可以将一个方法定义为只读属性,也可以定义一个可读写的属性,并且可以实现属性删除。

@setter装饰器用于定义一个属性的setter方法,用于修改属性的值。使用@setter时,我们需要在@property装饰的属性方法之后,紧跟一 个setter方法,并使用@属性名.setter来装饰该方法

@setter通常用于以下场景:
当一个属性的值需要计算得到,而不是直接存储在类的属性中时,我们可以使用@setter来提供一个修改属性值的接口。
当某个属性的值需要经过一些处理后再进行存储时,我们可以使用@setter来自动执行处理操作

class Person:
    def __init__(self, name):
        self._name = name # 私有属性
    
    @property       
    def name(self):        # 读取私有属性
        return self._name
    
    @name.setter          # 可写属性 
    def name(self, value):
        self._name = value
    
    @name.deleter
    def name(self):      # 删除属性
        del self._name

person = Person("Alice")
print(person.name)  # 输出: Alice
del person.name
print(person.name)  # 抛出AttributeError异常,属性已被删除

@property 装饰器可以用于对属性的计算,使得通过访问属性时能够返回计算后的结果

class Rectangle:
    def __init__(self, width, height):
        self._width = width
        self._height = height

    @property
    def width(self):
        return self._width

    @width.setter
    def width(self, value):
        if value >= 0:
            self._width = value
        else:
            raise ValueError("Width must be a non-negative number.")

    @property
    def height(self):
        return self._height

    @height.setter
    def height(self, value):
        if value >= 0:
            self._height = value
        else:
            raise ValueError("Height must be a non-negative number.")

    @property
    def area(self):
        return self._width * self._height

    @property
    def perimeter(self):
        return 2 * (self._width + self._height)

rectangle = Rectangle(4, 5)
print(rectangle.width)  # Output: 4
print(rectangle.height)  # Output: 5
print(rectangle.area)  # Output: 20
print(rectangle.perimeter)  # Output: 18

rectangle.width = 6
rectangle.height = 8
print(rectangle.width)  # Output: 6
print(rectangle.height)  # Output: 8
print(rectangle.area)  # Output: 48
print(rectangle.perimeter)  # Output: 28


@abstractmethod 是一个装饰器,用于声明抽象方法。抽象方法是在基类中定义但没有具体实现的方法,它需要在派生类中进行具体的实现

注意事项:

抽象方法的主要目的是定义一个接口,规定了派生类必须提供的方法。它在面向对象设计中非常有用,因为它可以确保派生类遵循特定的接口约定。

需要注意的是,抽象方法只能在抽象基类中定义,而不能直接实例化抽象基类。因此,抽象基类本身无法被实例化,只能被用作其他类的基类。

抽象基类必须继承自 ABC 基类,并使用 @abstractmethod 装饰器标记抽象方法。

派生类必须实现基类中的抽象方法,否则会引发 TypeError。

from abc import ABC, abstractmethod # 第一步:导入 abc

# 第二步: 定义抽象基类 架构文件定义
class Animal(metaclass=abc.ABCMeta):  # 同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass

# 第三步:创建派生类并实现抽象方法  #实现文件定义
class Cat(Animal):  # 动物的形态之一:猫 
    def talk(self):
        print('This is a cat')
        
class Dog(Animal):  # 动物的形态之二:狗
    def talk(self):
        print('This is a dog')


c = Cat()#实现文件定义
d = Dog()

def func(obj): #架构文件定义
    obj.talk()  # 同一函数名,根据对象不同实现不同方法

func(c)  # 'This is a cat'#实现文件调用
func(d)  # 'This is a dog'

自定义装饰器

# """ 装饰器:格式规范 """
# 第一步:写装饰器函数 (使用python内置装饰器时可省略)


def decorator_name(f):      # f为被修饰的函数
    @wraps(f)
# @wraps()接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。
    def decorated(*args, **kwargs):     # 对函数f的装饰,即添加新功能
        if not can_run:
            return "Function will not run"
        return f(*args, **kwargs)
    return decorated

# 第二步:@装饰器在被装饰的函数func上方,得到被装饰后的func()


@decorator_name     # @是一种简写,代表了func = decorator_name(func)
def func():
    return("Function is running")


# 此时的 func()已经是被装饰后的func()
can_run = True
print(func())
# Output: Function is running

can_run = False
print(func())
# Output: Function will not run

Python 理解设计模式之委托模式

  1. 重写__getattr__方法,使得委托者获得处理者的属性。

  2. 判断该属性是否为可调用函数,如果不是则直接返回,如果是,则用 wrapper 封装为可调用对象

class Dog:
    def __init__(self, voice):
        self.voice = voice

    def __getattr__(self, name):
        """
        重写__getattr__方法
        获得相应的属性

        Arguments:
            name {str} -- 目标变量/函数名
        
        Returns:
            attr -- 处理者的变量/函数
        """

        attr = getattr(self.voice, name)

        if not callable(attr):
            return attr

        def wrapper(*args, **kwargs):
            return attr(*args, **kwargs)
        return wrapper 
class voice:
    def __init__(self):
        self.p1 = 'test'

    def words(self, something):
        print("voice: %s" % something)
        return "voice: %s" % something
if __name__ == '__main__':
    John = Dog(voice())
    John.words('汪汪')
if __name__ == '__main__':
    John = Dog(voice())
    John.voice.words('汪汪') 


#字段包括普通字段和静态字段
class Province:
    # 静态字段
    country = '中国'
    def __init__(self, name):
        # 普通字段
        self.name = name
# 直接访问普通字段
obj = Province('河北省')
print obj.name
# 直接访问静态字段
Province.country

class ObjectDict(dict):
    def __init__(self, *args, **kwargs):
        super(ObjectDict, self).__init__(*args, **kwargs)
 
    def __getattr__(self, name):
        value =  self[name]
        if isinstance(value, dict):
            value = ObjectDict(value)
        return value
 
if __name__ == '__main__':
    od = ObjectDict(asf={'a': 1}, d=True)
    print od.asf
    print od.asf.a
    print od.d
>>>{'a':1}
>>>1
>>>True
class Wrapper:
    def __init__(self, obj):
        self.wrapper = obj
        print self.wrapper
        print type(self.wrapper)
        print"-"*100
 
    def __getattr__(self, item):
        print("trace:", item)
        return getattr(self.wrapper, item)
 
 
if __name__ == '__main__':
    x = Wrapper([1, 2, 3, 4])
    x.append(35)
    x.remove(2)
    print(x.wrapper)  # [1,3,4,35]__init__(self,obj)方法中传入一个被委托对象。 通过重写__getattr__(self,item)方法,拦截外部对象的属性调用 在__getattr__(self,item)中,将拦截到的属性,让被委托对象去使用。 python 中的属性概念,和Java中的属性概念是不同的。Java中的属性,就是指类中定义的成员变量,绝对不包含方法。而在python中,任何能以obj.xx形式调用的东西,全部可以称为属性。无论是方法,还是变量,还是对象。 所以上述代码中调用x.append(N),实际上是让x的属性wrapper去调用append(N)方法

Python–迭代器和生成器

合并多个有序序列,再对整个有序序列进行迭

import headq
a = [1, 2, 3]
b = [4, 5, 6]
for c in headq.merge(a,b):
    print(c)

在不同的容器中进行迭代

from itertools import chain
a = [1, 2, 3]
b = [4, 5, 6]
for x in chain(a, b):
    print(x)

同时迭代多个序列

xpts = [1, 2, 3]
ypts = [4, 5, 6]
for x, y in zip(xpts, ypts):
    print(x, y)

.以索引-值对的形式迭代序列

mylist = ['a', 'b', 'c']
for index ,value in enumerate(mylist):
    print(index, value)

迭代所有可能的组合或者排列

items = ['a', 'b', 'c']
from itertools import permutations
for p in permutations(iterms):
    print(p)
from itertools import combinations
for c in combinations(iterms):
    print(c)

对迭代器做切片操作

import itertools
for x in itertools.islice(c, 10, 20):
    print(x)
#-*- encoding:utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
class A:
    def f_one(self, x):
        print"here is f_one"
        print"x=",x
        print"-"*100
 
    def f_two(self):
        print"here is f_two"
        print"-"*100
 
class B(A):
    def __init__(self):
        self._a = A()
 
    def f_three(self):
        pass
 
    def __getattr__(self, name):#相当于重写了__getattr__,利用__getattr_来实现委托的效果(其实委托就是甩锅的意思啦,B搞不定,甩锅给A)
        return getattr(self._a, name)
if __name__ == '__main__':
    b_test=B()
    x=6
    b_test.f_one(x)
    b_test.f_two()
 
class A(object):
  def __init__(self, x):
    self.x = x
 
  def hello(self):
    return 'hello func'
 
  def __getattr__(self, item):
    print('in __getattr__')
    return 100
 
  def __getattribute__(self, item):
    print('in __getattribute__')
    return super(A, self).__getattribute__(item)
 
a = A(10)
print(a.x)
print(a.y)
in __getattribute__
10
in __getattribute__
in __getattr__
100

可以看出,在获到对象属性时,getattribute()是一定会被调用的,无论属性存不存在,首先都会调用这个魔法方法。 如果调用像a.y这种不存在的对象时,调用__getattribute__()找不到y这个属性,就会再调用__getattr__()这个魔法方法,可以通过在这个方法里实 来设置属性不存在时的默认值。使用上面的getattr()方法获取属性时,也是同样的调用关系,只不过只有在getattr()带第三个参数作为默认值时,才会调用 getattr()方法

class Descriptor:
    def __get__(self, instance, owner):
        print("Getting the value")
        return instance._value
 
    def __set__(self, instance, value):
        print("Setting the value")
        instance._value = value
 
class MyClass:
    descriptor = Descriptor()
 
    def __init__(self, value):
        self._value = value
 
my_obj = MyClass(42)
print(my_obj.descriptor)  # 输出: Getting the value, 42
my_obj.descriptor = 100   # 输出: Setting the value
print(my_obj.descriptor)  # 输出: Getting the value, 100

在上述示例中,Descriptor 类是一个描述符,它定义了 get() 和 set() 方法。MyClass 类中的 descriptor 属性是一个描述符实例。当通过 my_obj.descriptor 访问该属性时,会触发 Descriptor 类中的 get() 方法,并返回 _value 属性的值。当给 my_obj.descriptor 赋值时,会触发 Descriptor 类中的 set() 方法,将新的值赋给 _value 属性

class Dict(dict):
    def __init__(self, *args, **kwargs):
       super(Dict, self).__init__(*args, **kwargs)

    def __getattr__(self, name):
        value =  self[name]
        if isinstance(value, dict):
            value = Dict(value)
        return value
obj = Dict(student={'age': 18}, name='ZhangSan')
print(obj)
print(obj.name)
print(obj.student)
print(obj.student.age)
"""
{'student': {'age': 18}, 'name': 'ZhangSan'}
ZhangSan
{'age': 18}
18
"""


class A(object):
    def __init__(self, value):
        print( "into __init__")
        self.value = value
 
    def __setattr__(self, name, value):
        print ("into __setattr__")
        if value == 10:
            print( "from __init__")
        object.__setattr__(self, name, value)
 
a = A(10)
"""
into __init__
into __setattr__
from __init__
"""
print(a.value)
#10


a.value = 20
"""
into __setattr__
"""
print(a.value)
#20

class MyClass:
    def __init__(self):
        self.attribute = "Hello"

    def __delattr__(self, name):
        print(f"Deleting attribute: {name}")
        # 调用父类的__delattr__方法来实际删除属性
        super().__delattr__(name)

# 创建一个MyClass的实例
obj = MyClass()

# 尝试删除一个属性
del obj.attribute
# 输出: Deleting attribute: attribute

# 尝试访问已删除的属性,将引发AttributeError
try:
    print(obj.attribute)
except AttributeError as e:
    print(e)  # 输出: 'MyClass' object has no attribute 'attribute'

a = A(20)
print(a.value)
del a.value
print(a.value)

"""
20
when can not find attribute into __getattr__
"""


网站公告

今日签到

点亮在社区的每一天
去签到