目录
- 用字典映射代替其他语言中switch case 语句
- 列表推导式
- None的特殊性与误区
- 自定义对象与bool的对应关系
- 装饰器的副作用
- 编程能力
- 海象运算符
- 用f关键字做字符串拼接
- 装饰器:@dataclass数据类
四、装饰器的副作用
1.副作用
无装饰器时:
def f1():
print(f1.__name__)
f1()
-->
f1 #f1()函数的名字,是f1
有装饰器时:
增加一个打印时间的功能
import time
def decorator(func): #老函数要作为参数传进装饰器中
def wrapper():
print(time.time()) #增加打印时间的逻辑
func() #调用老函数
#⭐上面新老逻辑的调用均是函数,必须有小括号()才能运行
return wrapper
@decorator
def f1(): #在格式缩进上,老函数与装饰器是平级的
print(f1.__name__)
f1()
-->
1651636361.2582045
wrapper #函数的名字发生了改变,成了wrapper,即装饰器中内嵌函数的名字
在某些情况下,函数的名字发生了变化,会影响代码。
无装饰器时:
def f1():
'''
This is f1
''' #增加一段函数的注释
print(f1.__name__)
print(help(f1)) #利用内置函数help查看f1的文档
-->
f1()
This is f1
None
有装饰器时:
import time
def decorator(func):
def wrapper():
print(time.time())
func()
return wrapper
@decorator
def f1():
'''
This is f1
''' #增加一段函数的注释
print(f1.__name__)
print(help(f1))
-->
wrapper() #不仅函数名字发生了改变
#函数的注释也没有了
None
由此可见:
加了装饰器后,不仅老函数的名字发生了改变,函数的注释也变没了。
当执行带装饰器的函数f1时,本质上是直接执行的wrapper(),而不是f1.所以。函数的名字会丢失。
七月老师:
python写框架时,会非常依赖函数的名字。因为不要滥用装饰器,否则会容易发生不可预知的错误。
2.那么,如何在加了装饰器后,函数的名字不会改变,依然保持原有的函数名呢?
导入另一个内置的装饰器wraps
作用:装饰器wraps
知道所传入函数f1的各种信息,并将其复制到闭包函数中
import time
from functools import wraps #从函数库里,导入另一个函数(装饰器)
def decorator(func):
@wraps(func) #位置:在原装饰器内部,在闭包函数的上面加,并传新参
def wrapper():
print(time.time())
func()
return wrapper
@decorator
def f1():
'''
This is f1
'''
print(f1.__name__)
f1()
-->
1651638131.3523476
f1 #函数的名字,又返回到了其本来的名字
import time
from functools import wraps #从函数库里,导入另一个函数(装饰器)
def decorator(func):
@wraps(func) #位置:在原装饰器内部,在闭包函数的上面加,并传新参
def wrapper():
print(time.time())
func()
return wrapper
@decorator
def f1():
'''
This is f1
'''
print(f1.__name__)
print(help(f1))
-->
f1()
This is f1
None
当执行带装饰器的函数f1时,本质上是直接执行的rwrappe(),而不是f1。所以,函数的名字会丢失。
而加了装饰器wraps
后,就将f1的各种信息,复制到了闭包函数rwrappe()中。
五、编程能力
1.什么是编程能力,凭什么你比别人强,你的薪水比别人高?
不是会的语言多 ,--------你会8门语言,别人会5门语言
不是会的框架多, --------你会7个框架,别人会4个框架
不是api、语法背的熟,
而是简单的东西,有深入的理解,并能应用之解决问题。
因此,学习编程的动机、目的,是为了解决问题,做出好的项目、产品来。
而不是面向面试编程。
算法、数据结构、框架源码的确对面试有所帮助,但这不是初衷。
七月老师:
很多人面试优秀,但动手很差;
而有的人面试一般,但动手能力很强。
2.如何提高编程能力?
做高标准的真实项目,非demo。
六、海象运算符
walrus operator,python 3.8 新增的运算符。
符号为:=
作用:对一个表达式(函数的调用)进行求值,完成后可以在一行内完成一个变量的定义、赋值操作。
七月老师:
有点像Go语言中的,声明并赋值。
例题1:
求字符串'python'
的长度?
a = 'python'
b = len(a)
print(b)
-->
6
例题2:
判断字符串'python'
的长度,如果大于5,就打印“长度大于5;真实的长度为”?
写法1:
把字符串的长度求出来,赋值给新变量b。
a = 'python'
b = len(a)
if b >5:
print('长度大于5;'+'真实的长度为'+str(b))
-->
长度大于5;真实的长度为6
写法2:
没有新变量b,以后每次都要用len函数。
a = 'python'
if len(a) >5:
print('长度大于5;'+'真实的长度为'+str(len(a))) #使用了两次len(a)
-->
长度大于5;真实的长度为6
写法3:
a = 'python'
if b := len(a) >5: #海象运算符,像赋值运算符一样,默认顺序最后
print('长度大于5;'+'真实的长度为'+str(b))
-->
长度大于5;真实的长度为True #注意运算符的优先级顺序
改正:
a = 'python'
if (b := len(a)) >5: #用下括号, 调整为我们想要的运算符顺序
print('长度大于5;'+'真实的长度为'+str(b))
-->
长度大于5;真实的长度为6
小结:
写法3 的优点:
针对写法1:省去了单独定义的那行`b = len(a)
`
针对写法2:避免了函数的重复求值,提高了性能,作用很大。
七、用f关键字做字符串拼接
1.以往的字符串拼接
a = 'python'
if (b := len(a)) >5:
print('长度大于5;'+'真实的长度为'+str(b))
-->
长度大于5;真实的长度为6
2.用f关键字做字符串拼接
f关键字,花括号{ }包裹动态变量
a = 'python'
if (b := len(a)) >5:
print(f'长度大于5;真实的长度为{b}') #f关键字,花括号包裹动态变量b
-->
长度大于5;真实的长度为6
当然,不止能拼接一个动态变量:
a = 'python'
if (b := len(a)) >5:
print(f'长度大于5;{a}真实的长度为{b}') #f关键字,花括号包裹动态变量b
-->
长度大于5;python真实的长度为6
优点:
- 不用加号+了,可读性更好
- 不用考虑数据类型的转型了
八、装饰器:@dataclass数据类
作用:自动生成构造函数,简化代码
1.以前的定义类时构造函数的写法
class Student():
def __init__(self,name,age,school_name):
self.name = name
self.age = age
self.school_name = school_name #显得呆板、机械
def test(self): #实例方法定义,必须传入self
print(self.name)
student = Student('7yue',18,'Tsinghua')
student.test()
-->
7yue
2.用数据类dataclass定义类的构造函数
from dataclasses import dataclass #从模块dataclasses中,导入装饰器dataclass
@dataclass #语法糖 @dataclass
class Student():
name : str #形参 : 形参类型
age : int
school_name : str
def test(self):
print(self.name)
student = Student('7yue',18,'Tsinghua')
student.test()
-->
7yue #结果跟上面的写法1相同
由此可见,写法2中的语法糖@dataclass
会自动生成之前的构造函数。这在实例化参数比较多时,会比较方便。
3.装饰器dataclass只能生成构造函数吗?
不是。还可以生成其他的魔术方法__repr__()
。
普通构造函数时的__repr__()
:
class Student():
def __init__(self,name,age,school_name):
self.name = name
self.age = age
self.school_name = school_name
def test(self):
print(self.name)
student = Student('7yue',18,'Tsinghua')
print(student.__repr__())
-->
<__main__.Student object at 0x0000014E825A7C40> #可读性差
使用装饰器dataclass的__repr__()
:
from dataclasses import dataclass #从模块dataclasses中,导入装饰器dataclass
@dataclass #语法糖 @dataclass
class Student():
name : str #形参 : 形参类型
age : int
school_name : str
def test(self):
print(self.name)
student = Student('7yue',18,'Tsinghua')
print(student.__repr__())
-->
Student(name='7yue', age=18, school_name='Tsinghua') #类似于像构造函数签名一样的文本
还可以生成eq
方法
4.以上生成的方法,可控吗?
可控。
关闭装饰器@dataclass生成构造函数init()
:
from dataclasses import dataclass
@dataclass(init = False) #关闭了装饰器@dataclass自动生成构造函数
class Student():
name : str
age : int
school_name : str
def test(self):
print(self.name)
student = Student('7yue',18,'Tsinghua')
student.test()
-->
error ❌ #所以,类下面没有了构造函数,实例化对象时,自然就传不了实参
关闭了装饰器@dataclass生成魔术方法__repr__()
:
from dataclasses import dataclass
@dataclass(repr = False)
class Student():
name : str
age : int
school_name : str
def test(self):
print(self.name)
student = Student('7yue',18,'Tsinghua')
print(student.__repr__())
-->
<__main__.Student object at 0x000001EC639DBCA0> #变成了默认的repr返回结果
Comments | NOTHING