python装饰器介绍(闭包>装饰器>内置@property>私有属性/方法调用)、上下文管理器todo

1、闭包

闭包Closure特点:
[闭包函数的本质其实是一个嵌套函数],
[内部函数对外部函数作用域里变量的引用]
外层函数为内层函数提供了一个运行环境, [内层函数被当成返回值返回给外层函数]

优点:加强封装、避免全局变量污染、[变量长期存储用于缓存]
缺点:内存消耗大、内存泄露(避免滥用)
使用场景:变量活下来、装饰器

举例1-1:
def outer(x):
    def inner(y):
        '''引用外部函数作用域变量x'''
        return x+y
    return inner

var=outer(2)
print(var(3))
print(outer.__closure__)#outer不是闭包  None
print(var.__closure__)#inner是闭包     
for i  in var.__closure__:
    print('closure',i.cell_contents)#x=2 保留了外部函数的x值,让它活了下来
del var
print(var(3))#报错应,del了

举例1-2:
def func(obj):
    def wrapper():
        obj[0]+=1
        #print('wrapper:',obj)
        return obj
    return wrapper
mylist=[1,2,3]
var=func(mylist)
var()  #[2,2,3]
var()  #[3,2,3]

举例2:修改闭包的局部变量  =====nonlocal
实现计数器功能temp=0
def outer(arg):
    temp = 10
    def inner():
        nonlocal temp
        _sum = temp + arg
        temp += 1   #在内函数中尝试改变temp的值  nonlocal
        return _sum
    return inner

var=outer(5)
print(var())
print(var())
======================其他global
def outer1():
    temp = 10
    def inner1():
        nonlocal temp
        global arg
        _sum = temp + arg
        temp += 1   #在内函数中尝试改变temp的值  nonlocal
        arg+=10
        return _sum
    return inner1
arg=0
var=outer1()
print(var())#agr=0 temp=10  10
print(var())#agr=10 temp=11  21
print(var())#agr=20 temp=12  32
==============局部、全局、nonlocal变量 区别=============
def scope_test():
    def do_local():
        spam = "local spam"
    def do_nonlocal():
        nonlocal  spam
        spam = "nonlocal spam"
    def do_global():
        global spam
        spam = "global spam"

    spam = "new spam"
    # do_local()#局部变量已死 不影响
    # print("After local assignmane:", spam)#new spam
    # do_nonlocal()#nonlocal修改了非全局变量spam
    # print("After nonlocal assignment:",spam)#nonlocal spam
    # do_global()#修改全局变量spam为global,但是你在函数里打印的还是非全局变量
    # print("After global assignment:",spam)#nonlocal spam



举例3:闭包陷阱  循环
不要返回任何循环变量,或者后续会发生变化的变量
def count():
    fs = []
    for i in range(1, 4):
        def func():
             return i*i
        fs.append(func)
    return fs
f1, f2, f3 = count()
print(f1())#9
print(f2())#9
print(f3())#9

一定要利用循环,实现1平方 2平方 3平方:
def count():#再嵌套1层
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
    return fs

f1,f2,f3=count()
print(f1())
print(f2())
print(f3())

2、装饰器

【装饰器】:不改变函数/类原有功能的情况下,给它添加新功能。
比如:第三方接口(不允许改动),但需要优化:给他加装饰器添加功能。
装饰器本身是个闭包,将前面的闭包 变量改成函数名func。
使用场景:log打印、性能测试(统计每个程序的运行时间、调用了多少接口)、权限校验

分类:普通装饰器、带参数装饰器
      被装饰函数带参数、不带参数

装饰器demo样式:add函数添加新功能decorator  
@decorator
def add(a,b):
    return a+b
============================================================
举例1:(普通装饰器-被装饰函数不带参数)
外层参数 函数名func=被装饰函数函数名mysum
里层参数 没有
里层return func() 被装饰函数调用

def decorator(func):
    def wrapper():
        print(f"调用的被装饰函数方法:{func.__name__}")
        return func()
    return wrapper
@decorator
def myprint():
    print('print ok')

myprint()
============================================================
举例2:(普通装饰器-被装饰函数带参数)
外层参数 函数名func=被装饰函数函数名mysum
里层参数 被装饰函数参数 a,b
里层return func() 被装饰函数调用
[不改变函数原有功能,对入参做了变换]
def mysum(a,b):
    return a+b
print(mysum(1,2))

'''下面改成平方和'''
def decorator(func):
    def wrapper(a,b):
        a=a**2
        b=b**2
        return func(a,b)
    return wrapper

@decorator
def mysum(a,b):
    return a+b

print(mysum(1,2))#装饰器调用or
print(decorator(mysum)(1,2))#非装饰器调用,注释@decorator。

补充*args **kwargs知识:
def func(*args,**kwargs):
    print(args,'\n',kwargs)
func(1,2,name='nancy',age='20')#(1,2)     {'name': 'nancy', 'age': '20'}

普通装饰器通用格式:
import time
def decorator(func):
    def wrapper(*args,**kwargs):
        fn=func(*args,**kwargs)
        return fn
    return wrapper
@decorator
def add(a,b):
    return a+b
print(add(2,3))
===========================================================
举例3:装饰器带参数,外面再包一层
def arg_func(sex):#多包一层接受装饰器参数
    def outer(func):
        def inner(*args,**kwargs):
            if sex=='woman':
                print("你可以生娃")
            elif sex=='man':
                print("你不可以生娃")
            return func(*args,**kwargs)
        return inner
    return outer  #多包一层 return outer

@arg_func(sex='man')
def man():
    print("好好上班")

@arg_func(sex='woman')
def woman():
    print("好好上班")

@arg_func(sex=None)
def add(a,b):
    print(a+b)
man()
woman()
add(3,4)

============装饰器修改被装饰函数属性问题 ====functools.warps函数===================
举例4:函数属性问题
因为使用装饰器, 原函数(即被装饰函数)的地址指向了装饰器所定义的内部inner函数
可以借助functools.warps函数, 避免属性篡改问题
from functools import wraps
def mylog(func):
    @wraps(func)#如果注释  {add.__name__}=inner, 加上是add.
    def inner(*args, **kwargs):
        res = func(*args, **kwargs)
        print(f"执行结果为: {res}")
        return res
    return inner

@mylog
def add(a, b):
    return a + b
print(f"add函数的名字: {add.__name__}")
print(f"add函数的注释信息: {add.__doc__}")

add(1, 2)
===========================================================
举例5:多个装饰器 执行顺序:有里到外
from functools import wraps
def mylog1(func):
    @wraps(func)#里面那层一定要加wrapps
    def inner(*args, **kwargs):
        res = func(*args, **kwargs)
        print(f"执行结果为: {res}")
        return res
    return inner

def mylog2(level="INFO"):
    def outter(func):
        @wraps(func)
        def inner(*args, **kwargs):
            res = func(*args, **kwargs)
            print(f"[{level}]函数{func.__name__}的执行结果为: {res}")
            return res
        return inner
    return outter

@mylog2(level="DEBUG")
@mylog1
def add(a, b):
    return a + b
add(1, 2)
#mylog2(level="ERROR")(mylog1(add))(1, 2)#注释掉装饰@@
===========================================================
多装饰器执行顺序
from functools import wraps
def login(func):
    print("login")
    @wraps(func)
    def wrapper(*args,**kwargs):
        print("登录前代码")
        fn=func(*args,**kwargs)
        print("登录后代码")
        return fn
    return wrapper

def authority(func):
    print("authority")
    @wraps(func)
    def wrapper(*args,**kwargs):
        print("鉴权前代码")
        fn=func(*args,**kwargs)
        print("鉴权后代码")
        return fn
    return wrapper
# @authority
# @login
def buy(a,b):
    '''装饰器:鉴权,判断是否登录'''
    return a+b

buy=authority(login(buy))
print(buy(2,4))#先执行login装饰器再执行authority装饰器,先调用authority的wrapper,再执行login的wrapper
===========================================================
>>>>>>>>>>>>>>>>>常见使用场景 举一反三<<<<<<<<<<<<<<<<<
举例:[log打印]
略 举例5

举例:[每个程序运行时间统计]
import time
def runtime(func):
    def inner():
        start = time.time()
        func()
        end = time.time()
        print('[{}]函数执行用时:{}秒'.format(func.__name__,(end-start)))
    return inner
@runtime
def case():
    time.sleep(2)
    
case()
举例:[调用接口数统计]
举例3:被调用次数
def counter(func):
    count=0
    def wrapper(*args,**kwargs):
        nonlocal count # nonlocal改变了外部(非全局)变量的值
        count+=1
        print(f"函数{func.__name__}被调用了{count}次")
        return func(*args,**kwargs)
    return wrapper
def add(a,b):
    return a+b
def say_hello():
    return "hello"
sum=counter(add)
hello=counter(say_hello)

hello()
hello()
sum(1,2)
sum(2,3)


举例:类装饰器__call__  类装饰器:带/不带参数
A-函数装饰器装饰函数
B-函数装饰器装饰类
C-类装饰器装饰函数
D-类装饰器装饰类
总结:函数作为装饰器,可以装饰函数、可以装饰类;
     类作为装饰器,   可以装饰函数、可以装饰类。
#####################################
'''1、函数-装饰函数'''
import time
from functools import wraps
def timer(fun):
    @wraps(fun)
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res=fun(*args,**kwargs)
        end_time = time.time()
        print("函数运行时间为:{}".format(end_time-start_time))
        return res
    return wrapper
@timer
def testfun(a,b):
    time.sleep(2)
    return a+b

print(testfun.__name__,testfun(1,2))

'''2、函数-装饰类'''
import time
from functools import wraps
def timer2(fun):
    @wraps(fun)
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res=fun(*args,**kwargs)
        end_time = time.time()
        print("函数运行时间为:{}".format(end_time-start_time))
        return res
    return wrapper
class ObjFun():
    @timer2
    def testfun(self,a,b):
        time.sleep(2)
        return a+b
@timer2
class ObjFun2():
    def testfun(self):
        pass
o=ObjFun()
print(ObjFun.testfun.__name__,o.testfun(2,5))

oo=ObjFun2()
print(ObjFun2.__name__)

import time
from functools import wraps
class Timer():
    def __init__(self,func):
        self.func=func
    def __call__(self, *args, **kwargs):
        start=time.time()
        res=self.func(*args,**kwargs)
        end = time.time()
        print(f'函数{self.func.__name__}执行耗时:{(end-start)}')
        return res
@Timer
def testfun(a,b):
    time.sleep(1)
    return a+b
print(testfun(11,22))

'''3、类函数(不带参数)-装饰函数'''
import time
from functools import wraps
class Timer():
    def __init__(self,func):
        self.func=func
    def __call__(self, *args, **kwargs):
        start=time.time()
        res=self.func(*args,**kwargs)
        end = time.time()
        print(f'函数{self.func.__name__}执行耗时:{(end-start)}')
        return res
@Timer
def testfun(a,b):
    time.sleep(1)
    return a+b
# print(testfun(11,22))
'''4、类函数(带参数)-装饰类'''
class Timer2():
    def __init__(self,coder):
        self.coder=coder
    def __call__(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            start=time.time()
            res=func(*args,**kwargs)
            end = time.time()
            print(f'方法{func.__name__}执行耗时:{(end-start)}',self.coder)
            return res
        return wrapper
class ObjFun2():
    @Timer2(coder='12345')
    def testfun2(self,a,b):
        time.sleep(1)
        return a+b
print(ObjFun2().testfun2(11,22))
print(ObjFun2().testfun2.__name__)
#####################################
类装饰-1:
import time
class timer():
    '''类装饰器和函数装饰器实现的效果是一样的,只不过类装饰器在内部的装饰函数是用__call__ 方法实现的'''
    def __init__(self, func):
        self.func = func

    def __call__(self):
        '''__call__作用:能够使类的实例像函数调用那样被调用'''
        star_time = time.time()
        self.func()
        end_time = time.time()
        total = end_time - star_time
        print("函数运行时间:", total)

@timer
def fun():
    time.sleep(2)

fun()
类装饰-2:
from functools import wraps
class Hint():
    '''类装饰器 不带参数'''
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, **kwargs):
        print('{} is running.'.format(self.func.__name__))
        return self.func(*args, **kwargs)

class Hint2():
    '''类装饰器 带参数'''
    def __init__(self, coder=None):
        self.coder = coder
    def __call__(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print('{} is running..Coder: {}'.format((func.__name__),self.coder))
            return func(*args, **kwargs)
        return wrapper

@Hint
@Hint2('12345')
def add(a,b):
    return a+b

print(add(1,2))

###############################################
何时执行装饰器:
被装饰函数和装饰器在同一个模块时,只有在明确调用被装饰函数时装饰器才被执行。
当被装饰函数和装饰器在不同的模块时,只要被装饰函数一经定义,装饰器就会立即执行,这一般在import导入时发生。
import time
def timer(fun):
    print("timer-out")
    def wrapper():
        start_time = time.time()
        fun()
        end_time = time.time()
        print("函数运行时间为:{}".format(end_time-start_time))
    return wrapper
@timer
def fun():
    time.sleep(1)
fun()#打印*1+运行时间  注释后打印*1
改成跨目录:
from temp import timer
import time

@timer
def fun():
    time.sleep(1)
# fun()#打印*2+运行时间  注释后打印*2
###############################################

#装饰器在自动化测试中运用:
log、登录token鉴权
举例:[权限校验]  暂略?

3、内置装饰器

-@property
实现功能: 为类中的属性(私有的属性)提供设置和获取的方法(公有的方法)
@property最大的好处就是在类中把一个方法变成属性调用.

python类没有真正意义的私有属性:
一种用单下划线,表示这个属性是类的私有属性,不希望被外部访问到,但还是可以被访问的;
第二种是双下划线,表示这个属性就是类的私有属性,只能在类中被使用,不可以在实例化的对象中去使用。但可以通过对象名._类名__xxx的特殊方式访问。所以@property运营而生。
class person():
    def __init__(self,name,age,sex):
        self.name=name #共有
        self._age=age  #表示私有成员
        self.__sex=sex #私有成员
    def __private(self):
        print(self.name)
    def public(self):
        print(self.name)

p=person('nancy',20,'female')
print(p.name,p._age,p._person__sex)#都可以实现访问、篡改
p.public()
p._person__private()

========================保证私有属性只读 不可篡改========
class Person():
    def __init__(self, name, age=18):
        self.name = name
        self.__age = age#私有属性

    @property
    def age(self):
        #属性只读
        return self.__age

xm = Person('xiaoming')
print(xm.name,xm.age)
# xm.age = 14  # 报错无法给年龄赋值
xm.name='xiaoli'
print(xm.name,xm.age)
=====================类属性 预处理=====================
class User():
    def __init__(self, name, age):
        self.name = name
        self._age = age
        # print(self.tag,self.age)
    @property
    def tag(self):
        return self.name + str(self._age)

    @property
    def age(self):
        return self._age + 5

user = User('xiao',5)
print(user.tag)
print(user.age)
===========================类属性 预处理前(+5)==============================
class User():
    def __init__(self, name, age):
        self.name = name
        self._age = age
    @property
    def age(self):#类属性 预处理
        return self._age
    @age.setter
    def age(self,n):#预处理前 操作+5
        self._age = n + 5
        return self._age

user = User('xiao',0)#
user.age = 1#n
print(user.age)#6
=======================综合:明文输入 明文输出=================================
#用户输入明文->转化为密文后存入->用户读取时先转化为明文再输出
class User():
    def __init__(self):
        self._password = ''   # 密文存储
    @property
    def password(self):
        '''2、预处理操作'''
        return self._password.lower() # 解密

    @password.setter
    def password(self,word):
        '''1、入参,预处理前 操作'''
        self._password = word.upper() # 加密

user = User()
user.password = 'abc'   #0、明文输入
print(user.password)    #3、明文输出
==================动态属性统一调用方式======================
1、非动态属性
class Student:
    def __init__(self,name):
        self.name=name
        self.score=None
    def set_score(self,new_score):
        if not isinstance(new_score,int):
            raise ValueError("score must be int")
        if new_score>=0 and new_score<=100:
            self.score=new_score
            return self.score
        else:
            raise ValueError("invald score")

mike=Student('mike')
mike.set_score(88)#[1、函数调用方法] set_score 限制分数输入范围
print(mike.name,mike.score)
mike.score=999#[2、实例属性调用方法] 还是有办法误输入999且调用方式不统一
print(mike.name,mike.score)
2、动态属性
class Student2:
    def __init__(self,name):
        self.name=name
        self._score=None
    @property
    def myscore(self):
        '''预处理操作'''
        return self._score

    @myscore.setter
    def myscore(self,new_score):
        '''预处理前操作'''
        if not isinstance(new_score,int):
            raise ValueError("score must be int")
        if new_score>=0 and new_score<=100:
            self._score=new_score
            return self._score
        else:
            raise ValueError("invald score")

Nancy = Student2('nancy')
Nancy.myscore=81#统一了调用方式 (有点牵强?Nancy._score还是可以调用和修改值呀?)
print(Nancy.myscore)
==========================================================
私有属性访问:
1、类方法获取
2、装饰器的函数调用
3、语法糖 @装饰器
class Person():
    def get_name(self):
        return self._name
    def set_name(self, name):
        self._name = name

p = Person()
p.set_name("chinablue")#1、常规方法类方法实现私有属性的改变
print(p.get_name())    #获取私有属性

p.name = "djtest"  #2、装饰器的函数调用
print(p.name)


class Person():
    def __init__(self, name, age=18):
        self.name = name
        self.__age = age#私有属性

    @property
    def age(self):
        #属性只读
        return self.__age

xm = Person('xiaoming')
print(xm.name,xm.age)
# xm.age = 14  # 报错无法给年龄赋值
xm.name='xiaoli'
print(xm.name,xm.age)
=========================================================
python中,类变量又叫做类属性
然而类属性有分为3个不同的类型
类属性,实例属性,局部变量。
class a:
    # 类属性
    hobby = 'play sport'
    def asdf(self):
        #实例变量
        self.age = 19
        #局部变量
        Name = 'Wu'

=======================***类属性 实例属性的 访问大全***==========
class Person:
    '''类属性的 私有/共有 访问'''
    country='China'
    __identity='gcd'
    def __init__(self,name,age):
        '''实例属性的 私有/共有 访问'''
        self.name=name
        self.__age=age
        self.sf='人'
    def get_info(self):
        print(f"姓名{self.name},年龄{self.__age},秘密身份{self.__class__.__identity},国籍{self.__class__.country}")
        print(f"姓名{self.name},年龄{self.__age},秘密身份{Person.__identity},国籍{Person.country}")
        print(f"姓名{self.name},年龄{self.__age},秘密身份{self._Person__identity},国籍{self.country}")

print(Person.country,Person._Person__identity)
Jack=Person('jack',20)
print(Jack.country,Jack._Person__identity)
print(Jack.name,Jack._Person__age)
Jack.get_info()
#print(student.__class__.country)#调用父类的 类属性(子obj.__class__.父类属性)
#print(self.__class__.country,self.country)#类里调用父类的 类属性

=========== 构造方法和实例方法 访问大全=====================
class Person:
    '''私有 共有 方法的调用'''

    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.sf='gcd'

    def get_info(self):
        print(f"public 姓名:{self.name},年龄:{self.age}")

    def __get_private_info(self):
        print(f"private 姓名:{self.name},年龄:{self.age}")

    def get_un(self):
        self.get_info()
        self.__get_private_info()
        # super()._Person__get_private()###############

    def get_notice(self, notice):
        return notice


class Student(Person):
    '''包括 在继承里 方法调用
        子类 无法调用私有属性和私有方法
    '''

    def __init__(self, name, age, sno):
        # super().__init__(name,age)
        # super(Student, self).__init__(name,age)
        Person.__init__(self, name, age)  # 调用父类init 3法
        self.sno = sno

    def s_get_un(self):
        print(self.sf)  # 继承父类的初始化属性
        self.get_un()

        print(self.get_notice('西施'))
        # 调用父类方法的2种方式: 后面再说区别!!!
        # super(子类, self).父类方法(参数)和父类.父类方法(self, 参数)'''
        print(super(Student, self).get_notice('无名氏'))  #print(super().get_notice('无名氏'))
        print(Person.get_notice(self, '无名氏'))

        # super().get_un()##########
        super()._Person__get_private_info()  # [强制调用父类私有方法]


Jack = Person('jack', 20)
Jack.get_info()
Jack._Person__get_private_info()#[强制调用父类私有方法]
Jack.get_un()

tom = Student('tom', 18, '07010801')
print(tom.name, tom.age, tom.sno)
print(Jack.name, Jack.age)
tom.s_get_un()


===================属性调用例2==============================
class Person:
    country="China"
    __identity="gcd"
    def __init__(self,name,age):
        self.name=name
        self.__age=age
    def print_attribute(self):
        # 1、类内属性各种调用
        print(self.name,self.__age)

        print(Person.country,Person.__identity)
        print(self.country,self.__identity)
        print(self.__class__.country,self.__class__.__identity)

class Teacher(Person):
    def __init__(self,name,age):
        '''如果子类有构造函数 记得要初始化父类的属性,不然子类调用父类属性报错!!'''
        super().__init__(name,age)
    def print_attribute(self):
        #3、类内-类内-调用父类属性
        print(self.name)
        print(self._Person__age)
        print(self.country,self.__class__.country,super().country)
        print(self._Person__identity)
p1=Person('ls',20)
p1.print_attribute()
#2、类外属性各种调用
print(p1.name,p1._Person__age)
print(Person.country,Person._Person__identity)
print(p1.country,p1._Person__identity)

jack=Teacher('jack',30)
jack.print_attribute()
#4、子类-类外-调用父类属性
print(jack.name,jack._Person__age)
print(jack.country,jack.__class__.country,jack._Person__identity)

========================__私有属性 类外可通过:对象名._类名__XXX访问,不建议====
一般私有属性规范访问方法:1、2、3
'''
①get( ) 和 set( )
②property 方法 (此方法需要借助于get、set方法)
③通过装饰器 装饰器:@property
'''
class Person():
    __country="China"
    def __init__(self,name):
        self.__name=name
    def get(self):
        '''1-1、get获取私有属性'''
        return self.__name
    def set(self,name):
        '''1-2、set重新赋值 get获取'''
        self.__name=name
    def __str__(self):
        return "hello"
    #2、借助property(方法1,方法2)
    namer = property(get, set)  # property里面放两个参数,get方法和set方法
    #@property装饰器

    @property
    def updatename(self):
        return self.__name
    @updatename.setter
    def myname(self,newname):
        self.__name = newname

p1=Person('nancy')
# print(p1)
# print(p1._Person__name,p1._Person__country,Person._Person__country)#不建议
# p1.get()
# p1.set('helen')
# p1.get()

# print(p1.namer)#2-调用get方法 获取对象属性
# p1.namer="王五"#2-调用set方法 设置对象属性
# print(p1.namer)

#3、@property来调用和修改私有属性
p1.myname='jessica'
print(p1.myname)


==========================多继承看super()和父类.父类方法(self, 参数) 区别===============
super保证多继承的时候,公共父类只调用1次,mro顺序

待拓展:装饰器 鉴权举例

上下文管理器:with

上下文管理器介绍  TODO

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值