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__方法

 

原文地址:https://www.cnblogs.com/GumpYan/p/14150509.html