python高级——闭包和装饰器

python高级

函数传递

函数也可以作为参数传递
python中都是对地址进行传递的,所以函数名中存放的是函数所在的空间地址
函数名()才是调用函数,执行函数中的代码
所以函数可以像普通变量一样传递,也可以作为参数使用
函数作为参数传递的例子:

def A():
    print('我是A')
def B(fun):
    print('我是B,我要调用参数的函数')
    fun()
if __name__ == '__main__':
    B(A)

闭包

概念

  1. 函数嵌套
  2. 内部函数使用了外部函数的变量
    3.外部函数返回了内部函数
    我们把满足上面三个条件的内部函数称为闭包
    自定义闭包例子:
def fun_out(num1):
    def fun_inner(num2):
        return num1+num2
    print(num1)  # 1
    return fun_inner
if __name__ == '__main__':
    fi = fun_out(1)
    print(fi(2))  # 3

内部函数修改外部函数的变量

使用关键字nonlocal可以修改外部函数的变量,如果不使用该关键字直接修改则是重新定义了一个同名变量
例子:

def fun_out(num1):
    def fun_inner(num2):
      '''如果这样写在外部函数打印的number还是1,因为这里只是新定义了一个内部变量而不是对外部变量进行修改'''
        num1 = 23
        return num1+num2
    fun_inner(2)
    print(num1)  # 1
    return fun_inner
if __name__ == '__main__':
    fi = fun_out(1)
    print(fi(2))  # 23
——————————————————————————————————
def fun_out(num1):
    def fun_inner(num2):
        nonlocal num1
        num1 = 23
        return num1+num2
    fun_inner(2)
    print(num1)  # 1
    return fun_inner
if __name__ == '__main__':
    fi = fun_out(1)

装饰器

装饰器的作用

装饰器是在不改变原有函数源代码的情况下,给已有函数添加新的功能
装饰器本质上是一个闭包函数,外部函数传递函数,供内部函数调用
可以在原有函数上方通过@装饰器名字使用装饰器
在原有函数名的上方写@装饰器等同于fun_out(fun)()

无参装饰器

def fun_out(fun):
    def fun_inner():
        print('登陆')
        fun()
    return fun_inner
@fun_out
def fun():
    print('开始发布')
if __name__ == '__main__':
    fun()

装饰器装饰带参数的函数

内部函数的参数要和被装饰函数的参数保持一致

def fun_out(fun):
    def fun_inner(name):
        print('登陆')
        fun(name)
    return fun_inner
@fun_out
def fun(name):
    print('%s开始发布'%name)

if __name__ == '__main__':
    fun('zhangsan')

被装饰的函数带返回值

内部函数带返回值,返回被装饰函数的返回值

    def fun_inner(name):
        print('登陆')
        return fun(name)
    return fun_inner
@fun_out
def fun(name):
    print('%s开始发布'%name)
    return name

if __name__ == '__main__':
    print(fun('zhangsan'))

不定长参数

装饰器可以供多个函数自定义使用,装饰器的内部函数定义不定长参数,被装饰函数随机传参

def fun_out(fun):
    def fun_inner(*args,**kwrags):
        print('登陆')
        return fun(*args,**kwrags)
    return fun_inner
@fun_out
def fun(name):
    print('%s开始发布'%name)
    return name
@fun_out
def make(addr):
    print('发布于%s'%addr)
    return addr
if __name__ == '__main__':
    print(fun('zhangsan'))
    print(make('beijing'))
    '''
    登陆
    zhangsan开始发布
    zhangsan
    登陆
    发布于beijing
    beijing
    '''

多个装饰器装饰一个函数

多个装饰器作为多行写在函数名上方即可。离被装饰函数最近的那个装饰器先装饰函数

def fun_out1(fun):
    def fun_inner1(*args,**kwrags):
        print('登陆校验1')
        return fun(*args,**kwrags)
    return fun_inner1
def fun_out2(fun):
    def fun_inner2(*args,**kwrags):
        print('登陆校验2')
        return fun(*args,**kwrags)

    return fun_inner2
@fun_out2
@fun_out1
def fun(name):
    print('%s开始发布'%name)
    return name
if __name__ == '__main__':
    fun('zhangsan')
    '''
    登陆校验2
    登陆校验1
    zhangsan开始发布
    '''

带有参数的装饰器

装饰器的外部函数只能有一个参数,必须是被装饰的那个函数
通过在装饰器的外层再封装一层函数来实现传递参数,此时在使用@装饰器名(参数)来修饰函数时,第一步执行的是装饰器名(参数)得到内层装饰器后再装饰函数

def logging(flag):
    def fun_out(fun):
        def fun_inner(*args,**kwargs):
            if flag == '+':
                print('这是加法运算')
            elif flag == '-':
                print('这是剑法运算')
            else:
                print('只支持加减法运算')
            return fun(*args,**kwargs)
        return fun_inner
    return fun_out
@logging('+')
def fun(a,b):
    return a+b
if __name__ == '__main__':
    print(fun(1,4))

类装饰器

_call_魔术方法

如果类中定义了_call_魔术方法,那么这个类就可以像函数一样被调用了
将装饰器要装饰部分写在_call_中即可以实现类装饰器的功能

类定义部分:
class Dog():
    length = 50
    # 胖了多少斤
    set_height = -1
    def __init__(self,name,age,color,length):
        self.name = name
        self.age = age
        self.color = color
        self.length = length
    def __call__(self, *args, **kwargs):
        return '叮咚,调用了callable函数,使对象能像函数一样调用'
调用部分:
if __name__ == '__main__':
    # print(fun(1,4))
    jelly = Dog('果冻', '7个月了', 'white', 15)
    print(jelly())  # 叮咚,调用了callable函数,使对象能像函数一样调用

property属性

property属性是负责把类中的一个方法当作属性使用

装饰器方式使用

  1. 使用@property修饰获取属性的方法
  2. 使用@方法名.setter修饰设置属性的方法【这里的方法名为获取属性方法的方法名】
    调用时直接对象.获取属性的方法名不用加括号即可获取属性,对象.设置属性的方法名不用加括号=新的值即可更改原有值
类定义部分:
class Father:
   name = 'zhangsan'
   age = 20
   money = 500000
   __dono = '这是我自己的'
   small = '这是可以继承的属性'
   def __init__(self,name,age,money):
       self.name = name
       self.age = age
       self.money = money
   @property
   def get_done(self):
       return self.__dono
   @get_done.setter
   def set_done(self,dono):
       self.__dono = dono
调用部分:
if __name__ == '__main__':
   zhangsan = Father('zhangsan',45,5000000)
   print(zhangsan.get_done)  # 这是我自己的
   zhangsan.set_done = '改了'
   print(zhangsan.get_done)  # 改了

类属性方式使用

定义:类属性名 = property(获取值的方法名,设置值的方法名)
使用:对象.类属性名
对象.类属性名 = 值

类定义部分:
class Father:
    name = 'zhangsan'
    age = 20
    money = 500000
    __dono = '这是我自己的'
    small = '这是可以继承的属性'
    def __init__(self,name,age,money):
        self.name = name
        self.age = age
        self.money = money
    # @property
    def get_done(self):
        return self.__dono
    # @get_done.setter
    def set_done(self,dono):
        self.__dono = dono
    # 类属性的方式将方法当成属性调用
    done = property(get_done,set_done )
调用部分:
if __name__ == '__main__':
    zhangsan = Father('zhangsan',45,5000000)
    print(zhangsan.done)  # 这是我自己的
    zhangsan.done = '改了'
    print(zhangsan.done)  # 改了

with语句和上下文管理器

with语句

文件操作时使用with语句可以自动调用关闭文件操作,即使出现异常也会自动调用关闭文件操作。使得读写文件更加安全
文件使用完后如果不关闭,文件对象会一直占用操作系统的资源,并且操作系统同一时间能打开的文件数量是有限的,如果到达极限则不能打开新的文件
语法:
with 表达式 as 别名:
代码块
例如:

with open('test.txt','r') as f:
    f.read()

上下文管理器

with的安全性是靠上下文管理器来实现的,with创建的f文件对象就是一个上下文管理器对象
由下图可以看到,f对象实现了_enter_和_exit_方法

image.png

一个类只要实现了_enter_和_exit_方法,通过该类创建的对象,我们就称为上下文管理器
例子:

import time
class File:
    def __init__(self,file_name,file_model):
        self.file_name = file_name
        self.file_model = file_model

    # 上文方法
    def __enter__(self):
        print('这是上文')
        self.file = open(self.file_name,self.file_model)
        return self.file
    # 下文方法
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('这是下文')
        self.file.close()
if __name__ == '__main__':
    with File('test.txt','r') as f:
        while True:
            line = f.readline()
            time.sleep(5)
            if not line:
                break
            print(line)

在程序执行过程中我点击停止执行后在打印报错前将下文方法中的内容执行了


image.png

你可能感兴趣的:(python高级——闭包和装饰器)