11 Pythonic与Python杂记之三


目录

  • 用字典映射代替其他语言中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返回结果

声明:Jerry's Blog|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 11 Pythonic与Python杂记之三


Follow excellence, and success will chase you.