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与“上下文管理器”,函数触发 enter 和 exit
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 ----->追踪信息