第十章 设计模式/垃圾回收/装饰器/闭包

35 篇文章 3 订阅
25 篇文章 0 订阅

目录

一、设计模式

1.1 单例模式

1.2 工厂模式

1.3 抽象工厂模式

1.4 建造者

1.5 原型

二、垃圾回收

三、装饰器

3.1 变量作用域

3.2 变量解析规则

3.3 变量生存空间

3.4 嵌套函数

3.5 函数作为变量

3.6 闭包

3.7 装饰器的分类

3.8 装饰器学习九步法

3.8.1 第一步——最简单的函数,准备附加额外功能

3.8.2 第二步——使用装饰函数在函数执行前和执行后分别附加额外功能

3.8.3 第三步

3.8.4 第四步——使用内嵌包装函数来确保每次新函数都被调用

3.8.5 第五步——对带参数的函数进行装饰

3.8.6 第六步——对参数数量不确定的函数进行装饰

3.8.7 第七步——让装饰器带参数

3.8.8 第八步——让装饰器带 类 参数

3.8.9 第九步——装饰器带类参数,并分拆公共类到其他py文件中,同时演示了对一个函数应用多个装饰器

3.9 装饰器的顺序

3.10 内置装饰器


一、设计模式

设计模式是经过总结、优化的,对我们经常会碰到的一些编程问题的可重用解决方案。一个设计模式并不像一个类或一个库那样能够直接作用于我们的代码。反之,设计模式更为高级,它是一种必须在特定情形下实现的一种方法模板。设计模式不会绑定具体的编程语言。一个好的设计模式应该能够用大部分编程语言实现

三种最基本的设计模式:

  • 创建模式,提供实例化的方法,为适合的状况提供相应的对象创建方法。
  • 结构化模式,通常用来处理实体之间的关系,使得这些实体能够更好地协同工作。
  • 行为模式,用于在不同的实体建进行通信,为实体之间的通信提供更容易,更灵活的通信方法。

创建型

1. Factory Method(工厂方法)

2. Abstract Factory(抽象工厂)

3. Builder(建造者)

4. Prototype(原型)

5. Singleton(单例)

结构型

6. Adapter Class/Object(适配器)

7. Bridge(桥接)

8. Composite(组合)

9. Decorator(装饰)

10. Facade(外观)

11. Flyweight(享元)

12. Proxy(代理)

行为型

13. Interpreter(解释器)

14. Template Method(模板方法)

15. Chain of Responsibility(责任链)

16. Command(命令)

17. Iterator(迭代器)

18. Mediator(中介者)

19. Memento(备忘录)

20. Observer(观察者)

21. State(状态)

22. Strategy(策略)

23. Visitor(访问者)

1.1 单例模式

作用保证一个类只有一个实例并提供一个它的全局访问点。相当于全局变量,但防止了命名空间被污染。

使用场景

当类只需要有一个实例,比如说一个程序访问一个数据库,访问这个数据库的类只需要一个实例即可,防止随意的修改。python中的模块就是天然的单例模式,导入多次和导入一次是一样的;任务管理器也是单例模式。

class Singleton(object):
	def __new__(cls,*args,**kw):
	#如果cls这个类没有类变量_instance,则会执行if下的代码
		if not hasattr(cls,'_instance'):
			print(cls)
			obj = super(Singleton,cls)#找到父类的类对象:object
			cls._instance = obj.__new__(cls)
		return cls._instance
      
class MyClass(Singleton):
	a = 1
	def __init__(self,b):
		self.b = b

c1 = MyClass(1)
c2 = MyClass(2)
print(c1.b,c2.b)#打印:2,因为__init__执行了2次
c2.a = 3
print(c1.a) #打印:3
#c1和c2完全相同,可以用id()、==、is判断
print(id(c1))
print(id(c2))
print(c1 == c2) #判断值是否一致
print(c1 is c2)
"""
本质:做实例化的时候,判断有没有类变量_instance,
如果没有,则生成Singleton的父类对象object的实例,
赋值给_instance(只会做一次),返回cls._instance
如果有这个_instance类变量,直接返回cls._instance
"""

 一般不研究单例类的多重继承的情况。

class Singleton(object):
	def __new__(cls,*args,**kw):
		print(cls)
		if not hasattr(cls,'_instance'):
			print(cls)
			obj = super(Singleton,cls)
			cls._instance = obj.__new__(cls,*args,**kw)#如果实例化时有参数如myclass(1)时,则不能加,*args,**kw
		return cls._instance
      
class MyClass(Singleton):
	a = 1

class MyClass2(MyClass):
	b = 2

class MyClass3(Singleton):
	c = 3

four = Singleton()
one = MyClass()
two = MyClass2()
three = MyClass3()
print(one is two)#True
print(one is three)#True
print(one is four)#True

# 若按照如下顺序实例化,则结果完全不同
# two = MyClass2()
# one = MyClass()
# three = MyClass3()
# four = Singleton()
# print(one is two)#False
# print(one is three)#False
# print(one is four)#False

 补充:

多重继承:
class a
class b(a)
class c(b)
class d(c)

多继承:
class a
class b
class c(a,b)

1.2 工厂模式

本质:给一个函数传递不同的参数,然后生成不同的对象。 

class Dog:
	pass
	
class Cat:
	pass

def factory(name):
	if name == "Dog":
		return Dog()
	if name == "Cat":
		return Cat()


print(factory("Dog"))
print(factory("Cat"))

class ChinaGetter:
    """A simple localizer a la gettext"""
    def __init__(self):
        self.trans = dict(dog="小狗", cat="小猫")
 
    def get(self, msgid):
        """We'll punt if we don't have a translation"""
        try:
            return self.trans[msgid]
        except KeyError:
            return str(msgid)


class EnglishGetter:
    """Simply echoes the msg ids"""
    def get(self, msgid):
        return str(msgid)
 
 
def get_localizer(language="English"):# 工厂函数
    """The factory method"""
    languages = dict(English=EnglishGetter, China=ChinaGetter)
    return languages[language]()# 通过参数生成指定实例
 
# Create our localizers
e, g = get_localizer("English"), get_localizer("China")
# Localize some text
for msgid in "dog parrot cat bear".split():
	print(e.get(msgid), g.get(msgid))

1.3 抽象工厂模式

给一个工厂类,设定工厂类中的变量为某个具体的工厂类实例通过此方式实现不同实例的创建

import random
 
class PetShop:
    """A pet shop"""
 
    def __init__(self, animal_factory=None):
        """pet_factory is our abstract factory.
        We can set it at will."""
 
        self.pet_factory = animal_factory
 
    def show_pet(self):
        """Creates and shows a pet using the
        abstract factory"""
 
        pet = self.pet_factory.get_pet()
        print("This is a lovely", str(pet))
        print("It says", pet.speak())
        print("It eats", self.pet_factory.get_food())
 
 
# Stuff that our factory makes
class Dog:
    def speak(self):
        return "woof"
 
    def __str__(self):
        return "Dog"

 
class Cat:
    def speak(self):
        return "meow"
 
    def __str__(self):
        return "Cat"
 
 
# Factory classes
class DogFactory:
    def get_pet(self):
        return Dog()
 
    def get_food(self):
        return "dog food"
 
 
class CatFactory:
    def get_pet(self):
        return Cat()
 
    def get_food(self):
        return "cat food"
 
 
# Create the proper family
def get_factory():
    """Let's be dynamic!"""
    return random.choice([DogFactory, CatFactory])()
 
 
# Show pets with various factories
if __name__ == "__main__":
    shop = PetShop()
    for i in range(3):
        shop.pet_factory = get_factory()
        shop.show_pet()
        print("=" * 20)

1.4 建造者

需求,画人物,要求画一个人的头,左手,右手,左脚,右脚和身体,画一个瘦子,一个胖子

不使用设计模式:

if __name__=='__name__':
    print ('画左手')
    print ('画右手')
    print ('画左脚')
    print ('画右脚')
    print ('画胖身体')

    print ('画左手')
    print ('画右手')
    print ('画左脚')
    print ('画右脚')
    print ('画瘦身体')

这样写的缺点每画一个人,都要依次得画他的六个部位,这些部位有一些事可以重用的,所以调用起来会比较繁琐,而且客户调用的时候可能会忘记画其中的一个部位,所以容易出错。

使用建造者模式:

from abc import ABCMeta, abstractmethod
#abc的模块主要是创建抽象方法装饰器,类似java的interface接口,不能实现具体实现,只能定义包含哪些方法。

class Builder():
    __metaclass__ = ABCMeta

    @abstractmethod
    def draw_left_arm(self):
        pass

    @abstractmethod
    def draw_right_arm(self):
        pass

    @abstractmethod
    def draw_left_foot(self):
        pass

    @abstractmethod
    def draw_right_foot(self):
        pass

    @abstractmethod
    def draw_head(self):
        pass

    @abstractmethod
    def draw_body(self):
        pass


class Thin(Builder):
    def draw_left_arm(self):
        print ('画左手')

    def draw_right_arm(self):
        print ('画右手')

    def draw_left_foot(self):
        print ('画左脚')

    def draw_right_foot(self):
        print ('画右脚')

    def draw_head(self):
        print ('画头')

    def draw_body(self):
        print ('画瘦身体')


class Fat(Builder):
    def draw_left_arm(self):
        print ('画左手')

    def draw_right_arm(self):
        print ('画右手')

    def draw_left_foot(self):
        print ('画左脚')

    def draw_right_foot(self):
        print ('画右脚')

    def draw_head(self):
        print ('画头')

    def draw_body(self):
        print ('画胖身体')


class Director():
    def __init__(self, person):
        self.person=person

    def draw(self):
        self.person.draw_left_arm()
        self.person.draw_right_arm()
        self.person.draw_left_foot()
        self.person.draw_right_foot()
        self.person.draw_head()
        self.person.draw_body()


if __name__=='__main__':
    thin=Thin()
    fat=Fat()
    director_thin=Director(thin)
    director_thin.draw()
    director_fat=Director(fat)
    director_fat.draw()

建造一个抽象的类Builder,定义画六个部位的方法,每画一种人,就新建一个继承Builder的类,这样新建的类就必须要实现Builder的所有方法,这里主要运用了抽象方法的特性,父类定义了几个抽象的方法,子类必须要实现这些方法,否则就报错,这里解决了会漏画一个部位的问题。建造一个指挥者类Director,输入一个Builder类,定义一个draw的方法,把画这六个部位的方法调用都放在里面,这样调用起来就不会繁琐了。

简单来说就是把复杂类的实例生成,和具体实例的使用进行了完全的分离,谁也不会干扰谁的使用。

1.5 原型

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
适用场景:
当要实例化的类是在运行时刻指定时,可以使用此设计模式,先创建实例,然后注册到原型类实例中,通过clone方法获取刚才创建的实例,并根据使用需要,新增实例属性,来进行实例变量的扩展。 
例如:原型模式的实现:
在使用某OA系统时,有些岗位的员工发现他们每周的工作都大同小异,因此在填写工作周报时很多内容都是重复的,为了提高工作周报的创建效率,大家迫切地希望有一种机制能够快速创建相同或者相似的周报,包括创建周报的附件。试使用原型模式对该OA系统中的工作周报创建模块进行改进。

import copy
 
class Prototype:
    def __init__(self):
        self._objects = {}
 
    def register_object(self, name, obj):
        """Register an object"""
        self._objects[name] = obj
 
    def unregister_object(self, name):
        """Unregister an object"""
        del self._objects[name]
 
    def clone(self, name, **attr):
        """Clone a registered object and update inner attributes dictionary"""
        obj = copy.deepcopy(self._objects.get(name))
        obj.__dict__.update(attr) #增加这个对象的属性
        return obj
 
 
def main():
    class A:
        def __str__(self):
            return "I am A"
 
    a = A()
    prototype = Prototype()
    prototype.register_object('a', a)
    b = prototype.clone('a', a=1, b=2, c=3)
 
    print(a)
    print(b.a, b.b, b.c)
 
 
if __name__ == '__main__':
    main()


 

补充:

MVC:
M:module:模型,通过类的方式,从数据库取数据
V:view:视图,模板
C:controller,调用m从数据库或者文件取数据,存到变量里面

二、垃圾回收

python程序本身就是一个进程。操作系统会给进程分配一块内存空间,资源都是有限的,一直存一直存不释放的话程序就会爆掉,进程就死掉了。回收将程序中不再使用的数据清除,以便后续使用。

原理:

同Java语言一样,Python使用了引用计数这一简单技术来追踪内存中的对象。在Python内部记录着所有使用中的对象各有多少引用。一个内部跟踪变量,称为一个引用计数器。当对象被创建时,就创建了一个引用计数,当这个对象不再需要时,这个对象的引用计数变为0 时,它被垃圾回收。但是回收不是"立即"的,由解释器在适当的时机,将垃圾对象占用的内存空间回收。

import sys 
print (sys.getrefcount(500111)) #打印:3
a = 5001111 # 创建对象<5001111> 
print (sys.getrefcount(a)) #打印:4
b = a # 增加引用,<5001111>的计数 
print (sys.getrefcount(5001111 )) #打印:5
c = [b] # 增加引用. <5001111>的计数 
print (sys.getrefcount(5001111)) #打印:6
del a # 减少引用<5001111>的计数 
print (sys.getrefcount(5001111)) #打印:5
b = 100 # 减少引用<5001111>的计数 
print (sys.getrefcount(5001111 )) #打印:4
c[0] = -1 # 减少引用<5001111>的计数 
print (sys.getrefcount(5001111))#打印:3

析构函数 __del__ ,__del__在对象销毁的时候被调用,当对象不再被使用时,__del__方法运行

import time 
class Point: 
	def __init__(self, x=0, y=0): 
		self.x = x 
		self.y = y 
		
	def __del__(self): 
		class_name = self.__class__.__name__ 
		print (class_name, "销毁") 


pt1 = Point() 
pt2 = pt1 
pt3 = pt1 
print (id(pt1), id(pt2), id(pt3)) # 打印对象的id 
del pt1 
del pt2 
# time.sleep(10) 
del pt3

 循环引用的回收

垃圾回收机制不仅针对引用计数为0的对象,同样也可以处理循环引用的情况。循环引用指的是,两个对象相互引用,但是没有其他变量引用他们。这种情况下,仅使用引用计数是不够的。Python 的垃圾收集器实际上是一个引用计数器和一个循环垃圾收集器。作为引用计数的补充,垃圾收集器也会留心被分配的总量很大(及未通过引用计数销毁的那些)的对象。在这种情况下,解释器会暂停下来,试图清理所有未引用的循环。
class LeakTest:
    def __init__(self):
        self.a = None
        self.b = None
        print ("object = %d born here." % id(self))


A = LeakTest()
B = LeakTest()
A.a = B
B.b = A

import sys

print (sys.getrefcount(A))
print (sys.getrefcount(B))

del A
try:
    print (sys.getrefcount(A))
except Exception as e:
    print (e)

del B
try:
    print (sys.getrefcount(B))
except Exception as e:
	print (e)

三、装饰器

在代码运行期间在不改变原函数定义的基础上,动态给该函数增加功能的方式, 称之为装饰器(Decorator)。

装饰器是一个很著名的设计模式,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。

好处:能够给所有的函数增加一些额外的功能。
本质:@deco是个装饰函数函数,他需要返回一个闭包。闭包由_deco函数对象+原函数对象(闭包变量)。_deco函数对象本质:执行了附加的功能,并且执行了原函数。

3.1 变量作用域

在python中,函数会创建一个自己的作用域,或叫命名空间。这意味着在函数内部访问某个变量时,函数会优先在自己的命名空间中寻找。

outerVar= "this is a global variable"
def test():
	innerVar = "this is a Local variable"
	print ("local variables :")
	print (locals())
	print ("global variables :")
	print (globals()) 

test()

内置函数globals返回的是python解释器能知道的变量名称的字典(变量名:值),而locals函数返回的是函数test内部本地作用域中的变量名称字典。由此我们可以清晰的看出,函数都是由自己独立的命名空间的。

3.2 变量解析规则

在python的作用域规则里面,创建变量时一定会在当前作用域里创建同样的变量,但访问或修改变量时,会在当前作用域中查找该变量,如果没找到匹配的变量,就会依次向上在闭合作用域中进行查找,所以在函数中直接访问全局变量也是可以的。

outerVar = "this is a global variable"
def test() :
	innerVar = "this is a Local variable"
	print(outerVar)
	print(n)

n= 10
test()
#打印:this is a global variable
#10

3.3 变量生存空间

变量不仅仅是存在于一个个的命名空间中,它们还都有自己的生存周期,全局变量的生存周期是在整个程序执行期间有效,而局部变量的生存周期只在当前作用域中有效,一旦这个作用域不存在了,比如函数执行退出了,变量的生存周期就结束了

outerVar = "this is a global variable"
def test() :
	innerVar = "this is a Local variable"
test()
print (innerVar)#报NameError

3.4 嵌套函数

python允许创建嵌套函数。也就是说可以在函数里面定义函数,而且现有的作用域和变量生存周期依旧不变。

def outer() :
	name = "python"
	def inner() :
		print (name)
	return inner()#注意有括号

outer()#打印:python

说明:

在inner函数中,python解析器找一个叫name的本地变量,查找失败后会继续在上层的作用域里面寻找,这个上层作用域定义在outer函数里,python函数可以访问封闭作用域。

outer函数中最后一句,在调用inner函数,需要知道非常重要一点就是,inner也仅仅是一个遵循python变量解析规则的变量名,python解释器会优先在outer的作用域里面对变量名inner查找匹配的变量。把恰好是函数标识符的变量inner作为返回值返回,每次函数outer被调用的时候,函数inner都会被重新定义,如果它不被当做变量返回的话,每次执行过后它将不复存在。

在python里,函数就是对象,它也只是一些普通的值而已。也就是说你可以把函数像参数一样传递给其他的函数或者说从函数了里面返回函数

3.5 函数作为变量

def add(x, y): 
	return x + y 
	
def sub(x, y): 
	return x - y 

def apply(func, x, y): 
	return func(x, y) 


print (apply(add, 2, 1)) #打印:3
print (apply(sub, 2, 1)) #打印:1

说明:

apply函数准备接收一个函数的变量,它也只是一个普通的变量而已,和其他变量一样。然后我们调用传进来的函数:“()代表着调用的操作并且调用变量包含的值”。在函数外,我们也能看到传递函数并没有什么特殊的语法,函数名只是和其他变量一样的表标识符而已。

3.6 闭包

如果一个函数定义在另一个函数的作用域内,并且引用了外层函数的变量,则该函数称为闭包将内部的函数和函数中使用的外层函数变量打个包一起返回

闭包是Python所支持的一种特性,它让在非global scope定义的函数可以引用其外围空间中的变量,这些外围空间中被引用的变量叫做这个函数的环境变量。环境变量和这个非全局函数一起构成了闭包。
函数内部的变量作为闭包使用的变量:

#coding=utf-8
def outer() :
	name = "python"
	a = 100
	b = [1,2]
	def inner() :
		print(name)
		print(a)
	return inner

print(outer)#<function outer at 0x000001CDB85A5FC0>
print(outer())#<function outer.<locals>.inner at 0x000001CDB85A6050>
print(outer()())
#python
#100
#None
res = outer()     
res()
#python
#100
print (res. __closure__) # 查看闭包中的变量类型
#(<cell at 0x000001CDB8587C10: int object at 0x000001CDB8040D50>, <cell at 0x000001CDB8587B80: str object at 0x000001CDB85605B0>)

函数传入的局部变量作为闭包使用的变量:

def outer(name) :
	def inner() :
		print (name)
	return inner

res1 = outer("python")
print(res1.__closure__)
res2 = outer("java")
res1()#打印python
res2()#打印java

3.7 装饰器的分类

无参数decorator:生成一个新的装饰器函数
有参数decorator:装饰函数先处理参数,再生成一个新的装饰器函数,然后对函数进行装饰。
装饰器的具体定义: 
1、把要装饰的方法作为输入参数;
2、在函数体内可以进行任意的操作(可以想象其中会有很多应用场景);
3、只要确保最后返回一个可执行的函数即可(可以是原来的输入参数函 数,也可以是一个新函数)。

3.8 装饰器学习九步法

3.8.1 第一步——最简单的函数,准备附加额外功能

'''示例1: 最简单的函数,表示调用了两次'''
def myfunc():
	print ("myfunc() called.")
myfunc()#打印:myfunc() called.
myfunc()#打印:myfunc() called.

3.8.2 第二步——使用装饰函数在函数执行前和执行后分别附加额外功能

'''示例2: 替换函数(装饰)
装饰函数的参数是被装饰的函数对象,返回原函数对象
装饰的实质语句: myfunc = deco(myfunc)'''
def deco(func):
	print ("before myfunc() called.")
	func()
	print ("  after myfunc() called.")
	return func

def myfunc():
	print (" myfunc() called.")
myfunc = deco(myfunc)
myfunc()
myfunc()
"""打印:
before myfunc() called.
myfunc() called.
after myfunc() called.
myfunc() called.
myfunc() called.
"""

3.8.3 第三步

'''示例3: 使用语法糖@来装饰函数,相当于“myfunc = deco(myfunc)”
但发现新函数只在第一次被调用,且原函数多调用了一次'''
def deco(func):
	print ("before myfunc() called.")
	func()
	print ("  after myfunc() called.")
	return func

@deco #编译成:myfunc = deco(myfunc)
def myfunc():
	print (" myfunc() called.")
 
myfunc()
myfunc()
"""打印:
before myfunc() called.
myfunc() called.
after myfunc() called.
myfunc() called.
myfunc() called.
"""

3.8.4 第四步——使用内嵌包装函数来确保每次新函数都被调用

'''示例4: 使用内嵌包装函数来确保每次新函数都被调用,
内嵌包装函数的形参和返回值与原函数相同,
装饰函数返回内嵌包装函数对象''' 
def deco(func): 
	def _deco(): 
		print ("before myfunc() called.") 
		func() 
		print ("  after myfunc() called.") 
	return _deco
		
@deco #等价于myfunc = deco(myfunc)--->返回闭包:_deco+myfunc
def myfunc(): 
	print (" myfunc() called.") 
	return 'ok' 

print(myfunc)
myfunc() #_deco()+myfunc
myfunc()
"""
<function deco.<locals>._deco at 0x00000205263C60E0>
before myfunc() called.
 myfunc() called.
  after myfunc() called.
before myfunc() called.
 myfunc() called.
  after myfunc() called.
"""

3.8.5 第五步——对带参数的函数进行装饰

# -*- coding:utf-8 -*-
'''对带参数的函数进行装饰,内嵌包装函数的形参和返回值与原函数相同,
装饰函数返回内嵌包装函数对象'''

def deco(func):
	def _deco(a,b):
		print ("before myfunc() called.")
		ret = func(a,b)
		print ("  after myfunc() called.")
		return ret
	return _deco

@deco # 等价于闭包:_deco(a,b)+ myfunc
def myfunc(a,b):
	print (" myfunc(%s,%s) called."%(a,b))
	return a + b


r=myfunc(1,2)  #等价于闭包:_deco(1,2)+ myfunc
print(r)# 打印:3

"""
before myfunc() called.
 myfunc(1,2) called
  after myfunc() called.
"""

3.8.6 第六步——对参数数量不确定的函数进行装饰

# -*- coding:utf-8 -*-
'''对带参数数量不确定的函数进行装饰,
参数用(*args,**kwargs),自动适应变参和命名参数'''

def deco(func):
	def _deco(*args,**kwargs):
		print ("before %s called." % func.__name__)
		ret = func(*args,**kwargs)
		print (" after %s called. result: %s" % (func.__name__, ret))
		return ret
	return _deco

@deco
def myfunc(a,b):
	print (" myfunc(%s,%s) called."%(a,b))
	return a + b

@deco
def myfunc2(a,b,c):
	print (" myfunc2(%s,%s,%s) called."%(a,b,c))
	return a + b + c

myfunc(1,2)
myfunc2(1,2,c=3)

"""
before myfunc called.
 myfunc(1,2) called.
 after myfunc called. result: 3
before myfunc2 called.
 myfunc2(1,2,3) called.
 after myfunc2 called. result: 6
"""

3.8.7 第七步——让装饰器带参数

def deco(arg): 
	def _deco(func): 
		def __deco(): 
			print ("before %s called [%s]." % (func.__name__, arg)) 
			func() 
			print (" after %s called [%s]." % (func.__name__, arg)) 
		return __deco 
	return _deco

#deco("mymodule")(myfunc)
#1)deco("mymodule")--->闭包:_deco+arg
#2)_deco(myfunc)--->闭包:__deco+arg+func

@deco("mymodule") #等价于myfunc=deco("mymodule")(myfunc)
def myfunc(): 
	print (" myfunc() called.") 

@deco("module2") 
def myfunc2(): 
	print (" myfunc2() called.") 


myfunc() #__deco(),func-->myfunc,arg--->mymodule
myfunc2()

"""
before myfunc called [mymodule].
 myfunc() called.
 after myfunc called [mymodule].
before myfunc2 called [module2].
 myfunc2() called.
 after myfunc2 called [module2].
"""

3.8.8 第八步——让装饰器带 类 参数

class locker():
	def __init__(self):
		print("locker.__init__() is called")
 
	@staticmethod
	def acquire():# 加锁
		print("locker.acquire() is called.(静态方法)")

	@staticmethod
	def release():# 解锁
		print("    locker.release() is called.(不需要对象实例)")


def deco(cls):
	'''cls 必须实现acquire和release静态方法'''
	def _deco(func):
		def __deco():
			print("before %s called [%s]."%(func.__name__,cls))
			cls.acquire()
			try:
				return func()
			finally:
				cls.release()
		return __deco
	return _deco

# l = locker()
# @deco(l)
@deco(locker)#类的对象 myfunc = deco(locker)(myfunc)
def myfunc():
	print("  myfunc() called")

myfunc()
# myfunc()
"""
before myfunc called [<__main__.locker object at 0x0000022DD49F7A00>].
locker.acquire() is called.(静态方法)
  myfunc() called
    locker.release() is called.(不需要对象实例)
"""

3.8.9 第九步——装饰器带类参数,并分拆公共类到其他py文件中,同时演示了对一个函数应用多个装饰器

"""mylocker.py公共类"""
class mylocker: 
	def __init__(self): 
		print("mylocker.__init__() called.") 

	@staticmethod 
	def acquire(): 
		print("mylocker.acquire() called.") 

	@staticmethod 
	def unlock(): 
		print(" mylocker.unlock() called.") 


class lockerex(mylocker): 
	@staticmethod 
	def acquire(): 
		print("lockerex.acquire() called.") 

	@staticmethod 
	def unlock(): 
		print(" lockerex.unlock() called.")


def lockhelper(cls): 
	'''cls 必须实现acquire和release静态方法''' 
	def _deco(func): 
		def __deco(*args, **kwargs): 
			print("before %s called." % func.__name__) 
			cls.acquire() 
			try:
				return func(*args, **kwargs) 
			finally: 
				cls.unlock() 
		return __deco 
	return _deco

  

from mylocker import * 
class example: 
	@lockhelper(mylocker)# 等价于myfunc=lockhelper(mylocker)(myfunc)
	def myfunc(self): 
		print (" myfunc() called.") 
	
	@lockhelper(mylocker) 
	@lockhelper(lockerex) # myfunc2=lockhelper(mylocker)(lockhelper(lockerex)(myfunc2))
	def myfunc2(self, a, b): 
		print (" myfunc2() called.") 
		return a + b


if __name__=="__main__": 
	a = example() 
	# print(a.myfunc.__closure__)
	a.myfunc() 
	print(a.myfunc()) 
	print(a.myfunc2(1, 2)) 
	print(a.myfunc2(3, 4))

 

补充知识:

加锁的情况:给资源竞争的代码加锁,线程安全,运行慢
解锁:线程不安全,运行快

同步:1-->2-->3,每一步有严格的顺序。运行慢
异步:1\3\2,同时可以开始,谁先做谁后做没有关系,不影响结果。运行快

3.9 装饰器的顺序

@A 
@B 
@C 
def f(): 
	pass 

等价于: f = A(B(C(f))) #先执行c,b,a的上半部,f,执行c,b,a的下半部

3.10 内置装饰器

  • staticmethod:定义实例方法为静态方法
  • classmethod:定义实例方法为类方法 
  • property:对类属性的操作
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
闭包装饰器是一种特殊的装饰器,它使用闭包的概念来实现。闭包是指一个函数可以访问并操作其外部函数中定义的变量。在Python中,闭包装饰器可以用于给函数添加额外的功能,同保持函数的原始定义不变。 引用中的示例展示了装饰器传参的形式。在这个例子中,outer函数是一个装饰器,它将inner函数作为子函数返回,并在inner函数中添加了额外的功能。通过使用@outer装饰器语法,我们可以在add函数上应用outer装饰器,从而在调用add函数执行装饰器中的代码。 引用中的示例展示了多层装饰器使用。在这个例子中,outer1和outer2函数分别是两个装饰器,他们都返回一个inner函数。通过使用@outer1和@outer2装饰器语法,我们可以在outers函数上应用这两个装饰器,并在调用outers函数按照装饰器的定义顺序执行相关的代码。 引用提供了关于Python闭包装饰器使用方法的总结。这篇文章通过示例代码详细介绍了闭包装饰器使用,对于学习和工作有一定的参考价值。 引用中的示例展示了装饰器的形式。在这个例子中,outer函数是一个装饰器,它将inner函数作为子函数返回,并在inner函数中添加了额外的功能。通过使用@outer装饰器语法,我们可以在add函数上应用outer装饰器,从而在调用add函数执行装饰器中的代码。 综上所述,Python闭包装饰器是一种利用闭包概念实现的特殊装饰器,可以用于给函数添加额外的功能。这种装饰器可以通过装饰器传参的形式、多层装饰器的形式或普通的装饰器形式来实现。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值