Python易错点3
35. 多个装饰器装饰一个函数
多个装饰器装饰一个函数时,执行时的顺序是:最先装饰的装饰器,最后一个执行。它遵循了先进后出这样一个规则
https://www.cnblogs.com/GumpYan/p/12290166.html
def set_fun1(func): print("set_fun1已被定义") # 打印用于验证在多个装饰器的情况下,多个装饰器之间的执行顺序 def call_fun1(*args, **kwargs): print("call_fun1执行了") # 当被装饰函数执行时,会打印 return func() return call_fun1 def set_fun2(func): print("set_fun2已被定义") def call_fun2(*args, **kwargs): print("call_fun2执行了") return func() return call_fun2 @set_fun2 @set_fun1 def test(): pass
运行结果:
运行过程:
1.修饰器本质上是一个函数,只不过它的传入参数同样是一个函数。因此依次加了set_fun1和set_fun2两个装饰器的原函数test()实际上相当于set_fun2(set_fun1(test))
2.下面进入这个复合函数。首先执行内层函数set_fun(test),因此第一个打印的是set_fun1已被定义,接下来要注意,在set_fun1中定义了一个call_fun函数,但是没有调用它的语句,即没有set_fun1()语句。因此该函数内的语句并没有立即执行,而是作为了返回值。
因此set_fun1内的语句都被作为输入参数传递到set_fun2内。
3.下一步执行set_fun2()函数内容,先打印出了set_fun2已被定义,返回值为call_fun2。由于更外层没有装饰器,因此接下来就将执行call_fun2中的内容,打印call_fun2执行了,接着执行func()函数,此时func()表示的set_fun1中的内容,即有set_fun1()语句了,因此跳到set_fun1中执行,打印call_fun1执行了。
https://blog.csdn.net/yyb19951015/article/details/83014969
比较下面的这个例子:和上面的区别
""" 如果装饰器是多层的,谁距离函数最近,就优先使用那个 """ def decorate1(func): print("--------> 1 start") def wrapper(*args, **kwargs): func() print("刷漆") print("---------> 1 end") return wrapper def decorate2(func): print("--------> 2 start") def wrapper(*args, **kwargs): func() print("铺地板") print("------------> 2 end") return wrapper @decorate2 @decorate1 def house(): print("我是毛坯房") house()
36. unittest模块的使用,类的各个函数的调用顺序
unittest是python内置的单元测试框架,具备编写用例、组织用例、执行用例、输出报告等自动化框架的条件。
unittest工作流程:编写TestCase,由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite,最后将运行的结果保存在TextTestResult中。
testCase执行顺序:setUpClass() -> setUp() -> test1() -> tearDown() -> setUp() -> test2() -> tearDown() ->…-> tearDownClass
37.Decimal和Round
37.1 decimal的quantitize
默认参数ROUND_HALF_EVEN
看小数点最后一位
1、大于5,直接进位
decimal.Decimal(‘3.146’).quantize(decimal.Decimal(‘0.00’))
Decimal(‘3.15’)
decimal.Decimal(‘3.136’).quantize(decimal.Decimal(‘0.00’))
Decimal(‘3.14’)
2、小于5,直接舍去
decimal.Decimal(‘3.143’).quantize(decimal.Decimal(‘0.00’))
Decimal(‘3.14’)
decimal.Decimal(‘3.133’).quantize(decimal.Decimal(‘0.00’))
Decimal(‘3.13’)
3、等于5,看奇偶(奇进,偶舍)
decimal.Decimal(‘3.155’).quantize(decimal.Decimal(‘0.00’))
Decimal(‘3.16’)
decimal.Decimal(‘3.125’).quantize(decimal.Decimal(‘0.00’))
Decimal(‘3.12’)
decimal.Decimal(‘3.165’).quantize(decimal.Decimal(‘0.00’))
Decimal(‘3.16’)
decimal.Decimal(‘3.175’).quantize(decimal.Decimal(‘0.00’))
Decimal(‘3.18’)
37.2 Round精确存储,奇进偶舍
奇进偶舍:
例如数 a.bcd,我们需要保留 2 位小数的话,要看小数点后第三位:
如果 d<5,直接舍去
如果 d>5,直接进位
如果 d == 5:
d 后面还有非 0 数字,例如 a.bcdef,f 非 0,那么要进位
d 后面没有数据,且 c 为偶数,那么不进位
d 后面没有数据,且 c 为奇数,那么要进位
# https://www.tr0y.wang/2019/04/08/Python%E5%9B%9B%E8%88%8D%E4%BA%94%E5%85%A5/
print(round(1.115, 2)) #1.11 #十进制小数转二进制时精度丢失的问题
print(round(0.375, 2)) #0.38 #奇进偶舍
from decimal import Decimal # decimal 是 Python 专门处理高精度的库
print(Decimal(1.115)) #1.1149999999999999911182158029987476766109466552734375
print(round(0.375, 2)) #0.38
# 精确存储,奇进偶舍
round(0.125, 2)
0.12
round(0.135, 2)
0.14
round(0.375, 2)
0.38
# 非精确表示,精度截断,实际存储值要小
round(1.115, 2)
1.11
round(2.675, 2)
2.67
如何判断是否是精确表示,貌似只能使用decimal打印输出一下了
from decimal import Decimal # decimal 是 Python 专门处理高精度的库
print(Decimal(1.115)) #1.1149999999999999911182158029987476766109466552734375
# Python3 - 执行精确的浮点数运算 # 总的来说, decimal 模块主要用在涉及到金融的领域。 在这类程序中,哪怕是一点小小的误差在计算过程中蔓延都是不允许的 a = 2.1 b = 4.2 c = a + b print(c) print(c == 6.3) # 6.300000000000001 # False from decimal import Decimal a = Decimal('2.1') b = Decimal('4.2') c = a + b print(c) print(c == Decimal('6.3')) # 6.3 # True nums = [1.23e+18, 1, -1.23e+18] print(sum(nums)) # 0.0 import math nums = [1.23e+18, 1, -1.23e+18] print(1.23e+5) # 123000.0 print(math.fsum(nums)) # 1.0
import decimal # 需要精确数值计算的场景,应使用decimal模块,且不要用浮点数构造Decimal from decimal import Decimal print('%.20f' % 3.14) # 输出3.14000000000000012434 print(Decimal('3.14')) # 精确的始终只用两位小数表示 decimal.getcontext().rounding=decimal.ROUND_HALF_UP #4舍5入 ROUND_HALF_DOWN 不入 c1=decimal.Decimal('2.135').quantize(decimal.Decimal('0.00')) print(c1)
运行结果:2.14
c2=decimal.Decimal('2.145').quantize(decimal.Decimal('0.00')) print(c2)
运行结果:2.14
38. Python中的下划线变量
(1)_xxx
"单下划线 " 开始的成员变量叫做保护变量,意思是只有类实例和子类实例能访问到这些变量,
需通过类提供的接口进行访问;不能用’from module import *'导入
(2)__xxx
类中的私有变量/方法名 (Python的函数也是对象,所以成员方法称为成员变量也行得通。),
" 双下划线 " 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。
(3)__xxx__
系统定义名字,前后均有一个“双下划线” 代表python里特殊方法专用的标识,如 __init__()
代表类的初始化函数。
https://www.cnblogs.com/GumpYan/p/12381705.html
39. 集合的一些操作
39.1
- 交集{5}
print({1, 2, 3, 4, 5} & {5, 6})
- 并集{1, 2, 3, 4, 5, 6}
print({1, 2, 3, 4, 5} | {5, 6})
- 差集{1, 2, 3, 4}
print({1, 2, 3, 4, 5} - {5, 6})
- 对称差分:两个集合的非共同元素{1, 2, 3, 4, 6} (set(a)-set(b))|(set(b)-set(a))
print({1, 2, 3, 4, 5} ^ {5, 6})
- and、or代表逻辑运算符
- and返回第一个False的值,如果没有False的值则返回最后一项的值
print({1, 2, 3, 4, 5} and {5, 6}) # {5, 6}
-
- or返回第一个True的值,如果没有True的值则返回最后一项的值
print({1, 2, 3, 4, 5} or {5, 6}) # {1, 2, 3, 4, 5}
异或在数学上就是对称差(Symmetric difference)
数学上,两个集合的对称差(Symmetric difference)是只属于其中一个集合,而不被两个集合同时包含。 例如:集合{1,2,3}和{3,4}的对称差为{1,2,4}。集合论中的这个运算相当于布尔逻辑中的异或运算。所以在Python里使用了异或的符号(^)表示,内置函数为symmetric_difference()
39.2 集合增删
1.添加元素add和update
# 1.添加元素 thisset = set(("Google", "Runoob", "Taobao")) thisset.add("Facebook") print(thisset) # 还有一个方法,也可以添加元素,且参数可以是列表,元组,字典等,语法格式如下: thisset.update({1, 3}) thisset.update([1, 4], [5, 6]) thisset.update({'a': 'aaa'}) print(thisset)
2.移除元素
# s.remove( x )
# 将元素 x 从集合 s 中移除,如果元素不存在,则会发生错误。
thisset = set(("Google", "Runoob", "Taobao")) thisset.remove("Taobao") print(thisset) # {'Google', 'Runoob'} # thisset.remove("Facebook") # 不存在会发生错误 # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # KeyError: 'Facebook'
# 此外还有一个方法也是移除集合中的元素,且如果元素不存在,不会发生错误。格式如下所示:
# s.discard( x )
thisset = set(("Google", "Runoob", "Taobao")) thisset.discard("Facebook") # 不存在不会发生错误 print(thisset)
运行结果:
{'Taobao', 'Google', 'Runoob'}
40. 元类
class A: pass print(type(1)) # <class 'int'> print(type(type(1))) # <class 'type'> print(type(int)) # <class 'type'> print(type(A)) # <class 'type'>
- 元类是类的类,常可以用在类工厂中;
- Python中所有的类都是对象,可以通过type( )来创建元类
- 在定义类时,可用过metaclass参数来指定此类的元类
- Python类语句执行时,会先查找其类本身的metaclass属性,如果没找到,会继续在父类中找,还没找到,则到模块中找,最后再用内置的type来创建此类对象
- 使用类、函数都可以当做元类,通常在__new__()方法中通过type来自定义自己的元类
- 从设计的复杂度来讲,尽量少用元类,多用普通类或函数
41. 字符串拼接时间对比
42. Python 实例方法、类方法和静态方法
https://blog.csdn.net/lihao21/article/details/79762681
43. [lambda x: x*i for i in range(4)] LEGB规则
首先看
再看:
fun = [lambda x: x*i for i in range(4)] for item in fun: print(item(1))
输出结果:3, 3, 3, 3
https://blog.csdn.net/qdPython/article/details/107938206?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-2&spm=1001.2101.3001.4242
这个问题涉及到了Python的闭包及延时绑定的知识(Python作用域)
在Python核心编程里,闭包的定义如下:
如果在一个内部函数里,对外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认定是闭包。
总结为三点:
1、是一个内嵌函数
2、对外部函数变量引用
3、外部函数返回内嵌函数
简单的闭包例子:
def counter(start_at=0): count = [start_at] def incr(): count[0] += 1 return count[0] return incr
上面的那道题,可以写成这样:
def func(): fun_list = [] for i in range(4): def foo(x): return x*i fun_list.append(foo) return fun_list for m in func(): print m(2)
m表示的是foo函数
当我们执行 m(2) 时,运行到foo()内部函数,发现变量 i 并不是foo()中的变量,于是就到外部函数func中寻找变量 i ,但此时外部的 for 已经循环完毕,最后的 i =3 。所以,每次执行m(2),i 的值都是 3 ,因此,最终结果会是 [6, 6, 6, 6] 。
我们打印i的值就知道了:
def func(): fun_list = [] for i in range(4): def foo(x): print("i的值:", i) return x*i fun_list.append(foo) return fun_list for m in func(): print(m(2))
当在foo()中添加 i=i 后,即:
def func(): fun_list = [] for i in range(4): def foo(x, i=i): return x*i fun_list.append(foo) return fun_list for m in func(): print m(2)
这样的话,for循环执行时,就已经把 i(0, 1, 2, 3) 的值传给了foo()函数,此时的 i 已经是foo()函数的内部变量,运行到foo()函数时,就不会到外部函数寻找变量 i ,直接运行
x*i(0, 1, 2, 3),因此最终结果会是 [0, 2, 4, 6] 。
44. python中的__del__方法
https://www.cnblogs.com/GumpYan/p/12369633.html
什么时机触发__del__()方法?
python解释器回收所有在这一次执行过程开辟的空间,只要没有引用了,一回收就会调用__del__()方法,即当一块空间没有任何引用了,就会默认执行__del__
python中对象的赋值就是地址的赋值
class Person: def __init__(self, name): self.name = name p = Person('Jack') p1 = p # 将p的地址给了p1 p2 = p
class Dog: def __del__(self): #当内存不需要的时候调用这个删除方法,python解释器自动调用 print(“英雄over”) dog1=Dog() #创建一个对象 dog2=dog1 del dog1 del dog2 print(“==========”)
输出为:
删除对象的意思就是这个对象所对应的内存空间被释放了,当dog1被删除了,dog2还在,引用计数减掉1而已,内存还不会被释放,当del dog2,内存空间的引用都被删了,就会调用__del__()方法
class Dog: def __del__(self): #当内存不需要的时候调用这个删除方法,python解释器自动调用 print(“英雄over”) dog1=Dog() #创建一个对象 dog2=dog1 del dog1 print(“==========”)
当删除了dog1,内存空间还没有结束,还不会调用__del__方法,当调用完最后一条语句时,内存空间被释放,调用__del__方法