python的装饰器,魔法属性,with与“上下文管理器”

Python 的闭包
def deco(x, y):
    def wrapper(z):
        return x+y+z
    return wrapper

d = deco(100, 200)

print(d(300))  // 600

思考:函数、匿名函数、闭包、对象 当做实参时 有什么区别?
1. 匿名函数能够完成基本的简单功能,,,传递是这个函数的引用 只有功能
2. 普通函数能够完成较为复杂的功能,,,传递是这个函数的引用 只有功能
3. 闭包能够将较为复杂的功能,,,传递是这个闭包中的函数以及数据,因此传递是功能+数据
4. 对象能够完成最为复杂的功能,,,传递是很多数据+很多功能,因此传递是功能+数据

装饰器

什么是property属性
  一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法

class Goods:
    @property
    def size(self):
        return 100

g = Goods()

print(g.size)  # 100
Python的property属性的功能是:property属性内部进行一系列的逻辑计算,最终将计算结果返回。
装饰器,特性,注解
对修改是封闭的,对扩展是开放的
def current_func(func):
    func()

def f1():
    print('This is a function-1')

def f2():
    print('This is a function-2')

current_func(f1)
current_func(f2)

输出结果:
This is a function-1
This is a function-2
新式类,具有三种@property装饰器
class Goods:
    """python3中默认继承object类
        以python2、3执行此程序的结果不同,因为只有在python3中才有@xxx.setter  @xxx.deleter
    """
    @property
    def size(self):
        return 100

    @size.setter
    def size(self, value):
        print("@size.setter")

    @size.deleter
    def size(self):
        print("@size.deleter")

g = Goods()

print(g.size)    自动执行 @property 修饰的 price 方法,并获取方法的返回值
g.size = 200     自动执行 @price.setter 修饰的 price 方法,并将 200 赋值给方法的参数
del g.size       自动执行 @price.deleter 修饰的 price 方法

只有在属性 定义 property 后才能定义 setter,deleter方法


上面代码定义三种装饰器 也可以改为下面的方式定义:
class Goods:
    def __init__(self):
        self.__money = 200

    def get_money(self):
        print("get __money")
        return self.__money

    def set_money(self, value):
        print("set __money")
        self.__money = value

    def del_size(self):
        print("del __money")
        del self.__money

    m = property(get_money, set_money, del_size)

g = Goods()

print(g.m)
g.m = 500
del g.m

打印结果:
get __money
200
set __money
del __money

自定义 [函数|类] 的装饰器

函数的装饰器,不修改函数调用方式,依旧可以修改输出
def deco(func):
    print("开始装饰------")
    def wrapper():
        print("验证1 2 3")
        func()
    return wrapper

         deco 函数就是 test 当成func传入deco,返回值再赋给test
@deco  # 等价于 test = deco(test)
def test():
    print("test function")

print("开始执行代码---")
test()

输出结果:
开始装饰------
开始执行代码---
验证1 2 3
test function

在Python执行到 @deco 时,装饰器就已经被装饰了
对于装饰器传递参数
def deco(func):
    def wrapper(name):
        print("验证1 2 3")
        func(name)
    return wrapper

@deco  # 等价于 test = deco(test)
def test(name):
    print("name = " + name)

test("libai")

打印结果:
验证1 2 3
name = libai
利用 *args 可以传入任意个参数
def decorator(func):
    def wrapper(*args):  // 拆包
        func(*args)
    return wrapper

@decorator
def f0():
    print("no args")

@decorator
def f1(name):
    print("name = " + name)

@decorator
def f2(name, age):
    print("name = " + name + ", age = " + str(age))

f0()
f1("libai")
f2('libai', 22)

输出结果:
no args
name = libai
name = libai, age = 22
装饰器接收返回值
def deco(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)  // 这里加上一个 return
    return wrapper

@deco
def test(name):
    print("name = " + name)
    return "hello"

ret = test("libai")
print(ret)

打印结果:
name = libai
hello
装饰器的最终形态,**kw 代表关键字参数,多余的关键字会被当作字典打印出来
def decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper
不破坏代码实现,实现代码复用性,可以多个装饰器堆叠使用
多个装饰器装饰同一个函数
def deco_1(func):
    print("----装饰器-1----")
    def wrapper(*args, **kwargs):
        print("----装饰器-1--功能验证----")
        return func(*args, **kwargs)
    return wrapper

def deco_2(func):
    print("----装饰器-2----")
    def wrapper(*args, **kwargs):
        print("----装饰器-2--功能验证----")
        return func(*args, **kwargs)
    return wrapper

@deco_1
@deco_2  // 先执行 deco_2,再执行 deco_1
def test(name):
    print("name = " + name)

test("libai")

打印结果:
----装饰器-2----
----装饰器-1----
----装饰器-1--功能验证----
----装饰器-2--功能验证----
name = libai

装饰器装饰类

class Test():
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("装饰器类的__call__方法")
        return self.func(*args, **kwargs)

@Test
def func(name):
    print("name = " + name)

func("libai")

输出结果:
装饰器类的__call__方法
name = libai
装饰器传参数
def set_level(level):
    def deco(func):
        def inner(*args, **kwargs):
            if level == 1:
                print("----权限级别--1--验证----")
            elif level == 2:
                print("----权限级别--3--验证----")
            else:
                print("----权限级别--3--验证----")
            func(*args, **kwargs)
        return inner
    return deco


@set_level(1)
def test(name):
    print("name = " + name)

test("1000")

打印结果:
----权限级别--1--验证----
name = 1000

装饰器,改成类型检测,如果检测到不匹配,就print 和不写入

def deco(**kwargs): #接收参数传入**kwargs
    def wrapper(obj): #返回的 wrapper 传入earth
        print('========================')
        for key,value in kwargs.items(): # .items() 格式为元组
            setattr(obj, key, Type(key, value))
        return obj
    print(kwargs)
    return wrapper

#类型传入检测
class Type:
    def __init__(self, key, except_type):
        self.key = key
        self.except_type = except_type
    def __get__(self, instance, owner):
        print('get 方法')
        print(instance)
        print(owner)
        return instance.__dict__[self.key]
    def __set__(self, instance, value):
        print('set 方法')
        print(instance)
        print(value)
        if not isinstance(value,self.except_type):
            print("必须传入 %s " %self.except_type)
            return
        instance.__dict__[self.key] = value
    def __delete__(self, instance):
        print('del 方法')
        print(instance)
        del instance.__dict__[self.key]

@deco(name=str,age=int) #name 传入类型 str,age传入类型 int
class earth:
    def __init__(self, name, age):
        self.name = name
        self.age = age

e = earth('libai', '23') #触发set方法

print('*************************************************')
print(earth.__dict__)
print(e.__dict__)

输出结果:
{'name': , 'age': }
========================
set 方法
<__main__.earth object at 0x0000025C38B4E080>
libai
set 方法
<__main__.earth object at 0x0000025C38B4E080>
23
必须传入  
*************************************************
{'__module__': '__main__', '__init__': , '__dict__': , '__weakref__': , '__doc__': None, 'name': <__main__.Type object at 0x0000025C38B4AF98>, 'age': <__main__.Type object at 0x0000025C38B4E048>}
{'name': 'libai'}

@property 就是把装饰的类或者函数当成参数传入 property(类) 中,执行完后把返回值再赋给所装饰的类,下面是自定义的 @property,与原生的@property 有类似的功能

class Lazyproperty:
    def __init__(self, func):
        self.func = func
        print('func = ', self.func)
    def __get__(self, instance, owner):
        print('instance = ', instance)
        print('owner = ', owner)
        # 以下这句 self.func(instance) 被执行并赋值给print打印时,
        # 会执行一遍 test 函数,这也就是为什么输出结果中会有两个 test被打印
        print('------------------>>>', self.func(instance))
        print('***************************************************')
        return self.func(instance)

class earth:
    def __init__(self):
        pass
    @Lazyproperty
    def test(self):
        print('test')
        return '你好吗?'

e = earth()
print(e.test)

输出结果:
func =  
instance =  <__main__.earth object at 0x0000021C699CAEB8>
owner =  
test
------------------>>> 你好吗?  # test 被打印两次
***************************************************
test
你好吗?

Python 的魔法属性

__doc__表示类的描述信息,该属性不能继承

class Foo:
    """ 描述类信息,这是用于看片的神奇 """
    def func(self):
        pass

class Son(Foo): # __doc__ 属性不能继承
    pass

f = Foo()
print(Foo.__dict__)
print(Son.__dict__)
print(f.__dict__)

输出结果:
{'__module__': '__main__', '__doc__': ' 描述类信息,这是用于看片的神奇 ', 'func': , '__dict__': , '__weakref__': }
{'__module__': '__main__', '__doc__': None}
{}

__module____class__

__module__ 表示当前操作的对象在哪个模块
__class__ 表示当前操作的对象的类是什么

test.py 模块
class Person(object):
    def __init__(self):
        self.name = 'laowang'

main.py模块
from test import Person
obj = Person()
print(obj.__module__)  # 输出 test 即:输出Person所在的模块
print(obj.__class__)  # 输出 test.Person 即:输出Person所在的类

__init__初始化方法

初始化方法,通过类创建对象时,自动触发执行
class Person:
    def __init__(self, name):
        self.name = name
        self.age = 18

obj = Person('laowang')  # 自动执行类中的 __init__ 方法

析构函数__del__当对象在内存中被释放时,自动触发执行,实例被系统回收时触发

当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,
因为此工作都是交给Python解释器来执行,所以,__del__的调用是由解释器在进行垃圾回收时自动触发执行的。

class earth:
    def __init__(self):
        self.name = 'libai'
    def __del__(self):
        print('del is work')
e = earth()
print(e.name)
del e.name    现在不会触发 __del__ 函数

print("program end")
程序结束时实例被系统回收,触发 __del__ 函数

打印结果:
libai
program end
del is work

__call__对象后面加括号,触发执行。

对象后面加括号,触发执行。
注:__init__方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Foo:
    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):
        print('__call__')


obj = Foo()    执行 __init__
obj()    执行 __call__,调用类 Foo 下的 __call__ 方法

__dict__类或对象中的所有属性

类或对象中的所有属性
类的实例属性属于对象;类中的类属性和方法等属于类,即:

class Province(object):
    country = 'China'

    def __init__(self, name, count):
        self.name = name
        self.count = count

    def func(self, *args, **kwargs):
        print('func')

获取类的属性,即:类属性、方法、
print(Province.__dict__)
输出:{'__dict__': , '__module__': '__main__', 'country': 'China', '__doc__': None, '__weakref__': , 'func': , '__init__': }

obj1 = Province('山东', 10000)
print(obj1.__dict__)
获取 对象obj1 的属性
输出:{'count': 10000, 'name': '山东'}

obj2 = Province('山西', 20000)
print(obj2.__dict__)
获取 对象obj1 的属性
输出:{'count': 20000, 'name': '山西'}

__str__在打印 对象 时,默认输出该方法的返回值。

如果一个类中定义了__str__方法,print()函数触发的是系统 __str__ 的执行,默认输出该方法的返回值。
class Foo:
    def __str__(self):
        return 'laowang'

obj = Foo()
print(obj)
输出:laowang
print 调用的就是__str__方法,__repr__是交互式界面返回的值
自定制__repr__方法,当__repr____str同时存在时,会执行__str__方法,当没有__str__时,执行__repr__
class test:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return '名字是 %s,年龄是 %s' %(self.name, self.age)

    def __repr__(self):
        return "return repr"

t = test('libai', 20)
print(t)

输出结果:
名字是 libai,年龄是 20

迭代器方法,__iter____next__

class Fib():
    def __init__(self):
        self.a = 0
        self.b = 1

    def __iter__(self): #把对象变成可迭代,可以看到,只调用一次迭代器 __iter__ 方法
        print("run __iter__ function")
        return self

    def __next__(self):
        self.a, self.b = self.b, self.a+self.b
        if self.a > 10:
            raise StopIteration('终止了') # for 接收到StopIteration异常,就停止调用next()
        return self.a,self.b

f = Fib()
print(next(f))
print(next(f))
print('==========================')
for i in f:
    print(i, end=", ")

打印结果:
(1, 1)
(1, 2)
==========================
run __iter__ function
(2, 3), (3, 5), (5, 8), (8, 13),

__getitem____setitem____delitem__

用于索引操作,如字典。以上分别表示获取、设置、删除数据
class Foo(object):

    def __getitem__(self, key):
        print('__getitem__', key)

    def __setitem__(self, key, value):
        print('__setitem__', key, value)

    def __delitem__(self, key):
        print('__delitem__', key)

obj = Foo()

result = obj['k1']      # 自动触发执行 __getitem__
obj['k2'] = 'laowang'   # 自动触发执行 __setitem__
del obj['k1']           # 自动触发执行 __delitem__

__getslice____setslice____delslice__

该三个方法用于分片操作,如:列表
class Foo(object):

    def __getslice__(self, i, j):
        print('__getslice__', i, j)

    def __setslice__(self, i, j, sequence):
        print('__setslice__', i, j)

    def __delslice__(self, i, j):
        print('__delslice__', i, j)

obj = Foo()

obj[-1:1]                   # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44]    # 自动触发执行 __setslice__
del obj[0:2]                # 自动触发执行 __delslice__

__getattribute__ 属性,可以防止程序奔溃

class Earth:
    def __init__(self, name):
        self.name = name

    def __getattr__(self, item):
        print("getattr is work,get the ", item)
    
不管调用什么属性都会触发 __getattribute__ 属性,存在 __getattribute__ 属性就不会触发 __getattr__ 属性
    def __getattribute__(self, item): 
        print('getattribute is work, get the ', item)
        raise AttributeError("抛出异常了") # 模拟抛出异常,防止程序奔溃,raise 触发 __getattr__ 

e = Earth('game')
e.name
e.none

输出结果:
getattribute is work, get the  name  #name 存在
getattr is work,get the  name
getattribute is work, get the  none  #none 不存在
getattr is work,get the  none

自定制__format__方法,格式化输出

format_dict = {
    'ymd' : '{0.year} {0.mon} {0.day}',
    'y-m-d' : '{0.year}-{0.mon}-{0.day}',
    'y:m:d' : '{0.year}:{0.mon}:{0.day}'
}
class Date:
    def __init__(self, year, mon, day):
        self.year = year
        self.mon = mon
        self.day = day
    def __format__(self, format_spec):
        print("my format, format_spec = '%s'" %format_spec)
        if format_spec and format_spec in format_dict:
            return format_dict[format_spec].format(self)
        else:
            return format_dict['ymd'].format(self)
d = Date(2018, 4, 18)
print('{0.year} {0.mon} {0.day}'.format(d)) #一般的格式化输出

print(format(d, 'none')) #自定义格式化输出
print(format(d, 'y-m-d'))
print(format(d, 'y:m:d'))

输出结果:
my format, format_spec = 'none'
2018 4 18
my format, format_spec = 'y-m-d'
2018-4-18
my format, format_spec = 'y:m:d'
2018:4:18

with与“上下文管理器”,函数触发 enterexit

with open("output.txt", "r") as f:
    f.write("Python之禅")


等价于下面的 try/finally
f = open("output.txt", "w")
try:
    f.write("python之禅")
except IOError:
    print("oops error")
finally:
    f.close()

一种更加简洁、优雅的方式就是用 with 关键字。open 方法的返回值赋值给变量 f,当离开 with 代码
块的时候,系统会自动调用 f.close() 方法, with 的作用和使用 try/finally 语句是一样的。
那么它的实现原理是什么?在讲 with 的原理前要涉及到另外一个概念,
就是上下文管理器(Context Manager)。

上下文管理器

任何实现了 __enter__() 和 __exit__() 方法的对象都可称之为上下文管理器,上下文管理器对象
可以使用 with 关键字。显然,文件(file)对象也实现了上下文管理器。

那么文件对象是如何实现这两个方法的呢?我们可以模拟实现一个自己的文件类,
让该类实现 __enter__() 和 __exit__() 方法。

class File():

    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode

    def __enter__(self):  #开始时执行
        print("entering")
        self.f = open(self.filename, self.mode)
        return self.f

    def __exit__(self, *args):  # 结束时执行
        print("will exit")
        self.f.close()

__enter__() 方法返回资源对象,这里就是你将要打开的那个文件对象,
__exit__() 方法处理一些清除工作。

因为 File 类实现了上下文管理器,现在就可以使用 with 语句了。

with File('out.txt', 'w') as f:
    print("writing")
    f.write('hello, python')
这样,你就无需显示地调用 close 方法了,由系统自动去调用,
哪怕中间遇到异常 close 方法也会被调用。

1.with File ---> 触发File.__enter__(), 拿到返回值
2.as f -----> f=返回值
3.with File as f  等同于 f=File.__enter__()
4.执行代码块

一:没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都为none
二:在有异常的情况下,从异常出现的位置直接触发__exit__
    a: 如果__exit__的返回值为True,代表吞掉了异常
    b: 如果__exit__的返回值不为True,代表吐出了异常
    c: __exit__的运行完毕就代表了整个with语句的执行完毕

总结
Python 提供了 with 语法用于简化资源操作的后续清除操作,是 try/finally 的替代方法,
实现原理建立在上下文管理器之上。

对于异常信息

>>> abc
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'abc' is not defined

NameError ----->class
name 'abc' is not defined ----->异常的值
Traceback ----->追踪信息

你可能感兴趣的:(python的装饰器,魔法属性,with与“上下文管理器”)