Python中的语法糖介绍
语法糖(syntactic sugar)是指一种语法形式,它并不影响语言的功能,但使得代码更加易读、简洁和优雅。在Python编程中使用Python语法糖,可提高代码可读性和编写效率,使得代码更加精炼清晰,减少冗余和复杂性。
1. 魔法方法(magic methods)
python中的魔法方法是以双下划线开头和结尾的特殊方法,例如 init 和 str。这些方法在对象生命周期的不同阶段或特定操作中被自动调用,从而允许编程时自定义对象的行为。
基础魔法方法
__init__(self, ...)
: 初始化对象,在创建实例时调用。__del__(self)
: 对象销毁时调用,用于释放资源。__call__(self)
:允许一个类的实例像函数一样被调用。
属性相关的魔法方法
属性相关的魔法方法主要用于定义对象的属性访问行为。这些魔法方法可以让你定制属性的读取、赋值、删除等操作。
__getattribute__(self, name)
: 当访问对象的属性时调用, 用于定制所有属性的访问行为。__getattr__(self, name)
: 在访问不存在的属性时调用。通常用于提供默认值或触发特定的行为。__setattr__(self, name, value)
: 在给对象的属性赋值时调用。用于定制属性赋值的行为。__delattr__(self, name)
: 在删除对象的属性时调用。用于定制属性删除的行为。__dir__(self)
:在调用 dir(obj) 时调用。用于定制对象的属性列表,返回实例所拥有的属性和方法。
2. 装饰器(decorators)
装饰器是一种通过包装目标函数来修改器其行为的特殊高阶函数,输入的参数是被装饰的函数。
装饰器可能对输入的函数做某些处理,返回的结果可能是1)被装饰的函数;2)另一函数;3)可调用对象。大部分装饰器是利用函数的闭包原理实现的。
内置装饰器
@property:让方法变为虚拟属性
@property
属性装饰器提供了一种让类方法变为虚拟属性的实现方式,利用属性装饰器,可实现基于方法定义类属性,控制属性的读取、赋值、删除等行为。
定义一个鸭子类:
class Duck:
def __init__(self,color):
self.color = color
def quack(self):
print(f"hi,this is a {self.color} duck")
调用quack方法时,需要实例化对象.方法名()的形式:
d = Duck('red')
d.quack() # hi,this is a red duck
@property
则可让被装饰的类方法通过属性的方式调用:
class Duck:
def __init__(self,color):
self.color = color
@property
def quack(self):
print(f"hi,this is a {self.color} duck")
# 用类属性的方式访问
d.quack # hi,this is a red duck
@property
还支持自定义属性赋值(setter)和删除(deleter):
class Duck:
def __init__(self,color):
self.color = color
@property
def quack(self):
print(f"hi,this is a {self.color} duck")
@quack.setter
def quack(self,new_color):
self.color = self.color + "&" + new_color
@quack.deleter
def quack(self):
raise RuntimeError("can not this cute duck!")
@quack.setter这段代码的作用是,设置给quack赋值时,改变Duck的color属性:
d = Duck('red')
d.color # 'red'
d.quack = 'blue' # 把quack当做属性赋值,相当于quack(new_color='blue')
d.color # 'red&blue'
@quack.deleter这段代码则设置了在删除quack方法时,抛出错误:
d = Duck('red')
del d.quack # RuntimeError: can not this cute duck!
@classmenthod:定义类方法
装饰器 @classmenthod
用于定义类方法。类方法是绑定到类而不是实例的方法,可以通过类名或实例名调用。在类方法中,第一个参数通常被命名为 cls
,表示类本身。使用 @classmethod
装饰器来定义类方法,可以使得在调用该方法时,类会被作为第一个参数传递给方法。这对于在方法中访问类级别的属性或执行与类相关的操作非常有用。
被 @classmenthod
装饰的类方法可以通过类名直接调用:
class Duck:
def __init__(self,color):
self.color = color
def quack(self):
print(f"hi,this is a {self.color} duck")
@classmethod
def create_random(cls):
import random
color = random.choice(['yellow','white','green'])
cls.color=color # 给类的color属性赋值
print(f"Class variable: {cls.color}")
return cls(color=color)
# 通过类名直接调用类方法
Duck.create_random() # Class variable: yellow
# <__main__.Duck at 0x2856afd2a58>
类方法也可以通过实例调用:
d = Duck('red')
d.create_random()
# Class variable: yellow
# <__main__.Duck at 0x28508003cc0>
@staticmethod: 定义静态方法
若类中某个方法不需要使用当前实例里的任何内容,可以用@staticmethod
将该方法装饰为静态方法。
静态方法不接受当前实例作为第一个位置参数,即方法的输入可以没有self
参数。
如下面的例子中定义get_name为静态方法,随机生成鸭子的名字:
class Duck:
def __init__(self,color):
self.color = color
def quack(self):
print(f"hi,this is a {self.color} duck")
@staticmethod
def get_name(): # get_name设为静态方法,不需要self参数
import random
repeats = random.randrange(1,10)
return ' '.join(['keda']*repeats)
输出结果为:
d = Duck('red')
# @staticmethod装饰后
d.get_name() # 'keda keda keda keda keda keda keda keda'
若get_name不使用@staticmethod装饰,则会抛出类型错误:
# 缺少self
d.get_name() # TypeError: get_name() takes 0 positional arguments but 1 was given
functools中的装饰器
functools 模块提供了一些用于创建和操作装饰器的函数,常用的包括wrap、lru_cache等。
functools wraps: 保留元数据
wraps
装饰器位于 functools 模块中,用于修饰其他装饰器函数。它的作用是将原始函数的元信息(如文档字符串、函数名)复制到装饰器函数,从而保留原函数的特性。
在装饰器包装目标函数的过程中,常会出现一些副作用,如丢失函数元数据:
from functools import wraps
def my_decorator(func):
"""this is my_decorator __doc__"""
def wrapper(*args,**kwargs):
"""this is wrapper __doc__"""
print(f"this is wrapper method")
print(f"Calling {func.__name__}")
res = func(*args,**kwargs)
print(f"{func.__name__} returned {res}")
return func(*args,**kwargs)
return wrapper
@my_decorator
def example_func():
"""this is example_func __doc__"""
print(f"Executing the example function")
return 1
result = example_func()
print(f"func name:{example_func.__name__}") # func name:wrapper
print(f"docstring:{example_func.__doc__}") # docstring:this is wrapper __doc__
使用@wraps装饰器则可解决元数据丢失的问题:
from functools import wraps
def my_decorator(func):
"""this is my_decorator __doc__"""
@wraps(func)
def wrapper(*args,**kwargs):
"""this is wrapper __doc__"""
print(f"this is wrapper method")
print(f"Calling {func.__name__}")
res = func(*args,**kwargs)
print(f"{func.__name__} returned {res}")
return func(*args,**kwargs)
return wrapper
@my_decorator
def example_func():
"""this is example_func __doc__"""
print(f"Executing the example function")
return 1
result = example_func()
print(f"func name:{example_func.__name__}") # func name:example_func
print(f"docstring:{example_func.__doc__}") # docstring:this is example_func __doc__
functools lru_cache: 缓存计算结果
functools 模块中的 lru_cache
装饰器用于实现缓存,可以有效地提高函数的执行速度,特别是对于需要消耗较多计算资源的函数。
使用lru_cache
装饰器时可传入可选的maxsize参数(默认值为128),表示当前函数最多保存的缓存结果的数量。当缓存结果数超过maxsize后,程序会基于最近最少使用算法丢弃旧缓存,释放内存。
下面给出一个在计算斐波那契数列中使用LRU缓存机制的例子:
from functools import lru_cache
@lru_cache(maxsize=3)
def fibonacci(n):
if n<=1:
return n
return fibonacci(n-1)+fibonacci(n-2)
fibonacci(6)
3. 推导式(comprehension)
列表推导式(list comprehension)
[x for x in range(8)] # [0, 1, 2, 3, 4, 5, 6, 7]
字典推导式(dict comprehension)
d={'a':1,'b':2,'c':3}
{k:v for k,v in d.items()} # {'a': 1, 'b': 2, 'c': 3}
生成器推导式(generator comprehension)
gen = (x**2 for x in range(8))
gen # <generator object <genexpr> at 0x00000285082342B0>
print(next(gen)) # 0
print(gen.__next__()) # 1
4. 上下文管理器
使用类来定义上下文管理器,只需在类中实现__enter__
和__exit__
魔法方法。__exit__
接收三个参数,exc_type
表示异常的类型,exc_value
表示异常对象,traceback
表示错误的堆栈对象。
class MyContextManager:
def __enter__(self):
print("Entering the context")
return 'Resource'
def __exit__(self,exc_type,exc_value,traceback):
print("Exiting the context")
with MyContextManager() as resource:
print("Inside the context, using resource: {resource}")
# Entering the context
# Inside the context, using resource: {resource}
# Exiting the context
其中__enter__
方法返回被管理的资源,而__exit__
方法在退出代码块时被调用。
5. 海象运算符
海象运算符(walrus operator)在python3,8版本中引入,赋值表达式使用海象运算符(:=)在单个表达式中同时对变量名进行赋值和计算,从而减少重复。当赋值表达式是一个较大表达式的子表达式时,必须用圆括号括起来。
海象表达式的语法形式是:
variable_name := expression
使用海象表达式可简化if-else、while语句等写法。
a = 6
if a>5:
print("do something")
# 使用海象表达式可简化代码为:
if a := 6 > 5:
print("do something")
6. 比较运算符
在其他语言中,小于和大于比较运算需要分开来表示,但在python中可以连写:
x,a,b = 4,3,6
if a<x<b:
print(x)
7. 数字的下划线写法
python中数字的下划线写法(Underscores in Numeric Literals)即使用下划线 _ 来分割大数字,是一种数字字面量的可读性改进,这个特性的引入旨在使长数字更易读,可以帮助人们更容易地理解数字的大小,而不会影响数字的实际值。
big_number = 6_666_000_000 # 大数用_分隔符表示更直观易读
print(big_number+1) # 等于6666000001
8. 匿名函数
使用lambda
关键字可以定义匿名函数;
multi = lambda x,y : x*y
multi(3,4) # 调用匿名函数
函数在Python中是一等对象,因此函数自身也可以作为函数参数来使用,如内置排序函数sorted
函数的用法:
l = [2,4,6,9,1,3]
sorted(l,key=lambda i:i % 3)
参考资料
《Python工匠:案例、技巧与工程实践》
《Python Cookbook》
《Fluent Python》
《The-Kaggle-Book》:https://github.com/PacktPublishing/The-Kaggle-Book
Syntactic Sugar in Python:https://medium.com/analytics-vidhya/syntactic-sugar-in-python-3e61d1ef2bbf