
Python 写多了会慢慢发现,语法本身藏着不少好用的东西,但不是每个人都会去翻文档把它们挖出来。下面整理了 30 个从基础到进阶的技巧,每条附带可直接跑的代码,按需取用。
Python 3.8 引入的 := 可以在表达式里直接赋值,省掉一次单独的赋值语句:
# 传统写法
lines = []
whileTrue:
line = input()
ifnotline:
break
lines.append(line)
# 使用海象运算符
lines = []
while (line := input()):
lines.append(line)类型提示不影响运行,但 IDE 的补全和 mypy 的静态检查都依赖它。写库或者多人协作时特别省事:
fromtypingimportOptional, List, Tuple
defprocess_data(
data: List[int],
threshold: Optional[int] = None
) ->Tuple[List[int], int]:
"""处理数据并返回结果和计数"""
ifthresholdisNone:
threshold = 0
filtered = [xforxindataifx>threshold]
returnfiltered, len(filtered)用字符串拼路径是老写法,pathlib 更直观,也跨平台:
frompathlibimportPath
data_dir = Path("data")
data_dir.mkdir(exist_ok=True)
file_path = data_dir/"output.txt"
file_path.write_text("Hello, Python!")
content = file_path.read_text()
print(content)访问字典里不存在的键会直接抛 KeyError,用 get 可以给个默认值兜底:
user_preferences = {"theme": "dark", "language": "zh"}
theme = user_preferences.get("theme", "light")
font_size = user_preferences.get("font_size", 14) # 返回默认值 14需要给字典里每个新键自动初始化一个默认值时,defaultdict 比手动判断 if key not in d 简洁很多:
fromcollectionsimportdefaultdict
text = "apple banana apple orange banana apple"
word_count = defaultdict(int)
forwordintext.split():
word_count[word] += 1
print(dict(word_count)) # {'apple': 3, 'banana': 2, 'orange': 1}遍历时需要索引,不用 range(len(...)),直接用 enumerate:
fruits = ["apple", "banana", "orange", "grape"]
fori, fruitinenumerate(fruits, start=1): # 从 1 开始计数
print(f"{i}. {fruit}")多个列表要一起遍历,用 zip 比手动管索引清楚:
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
subjects = ["Math", "Science", "English"]
forname, score, subjectinzip(names, scores, subjects):
print(f"{name} got {score} in {subject}")
# 顺手转成字典
result = dict(zip(names, scores))
print(result) # {'Alice': 85, 'Bob': 92, 'Charlie': 78}列表推导式可以嵌套,生成二维结构时比双层 for 循环紧凑:
multiplication_table = [[i*jforjinrange(1, 6)] foriinrange(1, 6)]
forrowinmultiplication_table:
print(row)
# [1, 2, 3, 4, 5]
# [2, 4, 6, 8, 10]
# ...判断序列里是否有满足条件的元素,或者全部满足:
numbers = [2, 4, 6, 8, 10]
all_even = all(n%2 == 0forninnumbers)
print(f"All even: {all_even}") # True
has_large = any(n>5forninnumbers)
print(f"Has number > 5: {has_large}") # Truesorted 的 key 参数接收 lambda,可以同时按多个字段排序:
students = [
{"name": "Alice", "score": 85, "age": 20},
{"name": "Bob", "score": 92, "age": 19},
{"name": "Charlie", "score": 85, "age": 21},
{"name": "David", "score": 78, "age": 20},
]
# 分数降序,年龄升序
sorted_students = sorted(
students,
key=lambdax: (-x["score"], x["age"])
)
forstudentinsorted_students:
print(f"{student['name']}: score={student['score']}, age={student['age']}")f-string 的格式化能力比很多人用到的强得多:
name = "Python"
version = 3.11
price = 0.0
print(f"Language: {name:>10}") # 右对齐,宽度 10
print(f"Version: {version:.2f}") # 保留 2 位小数
print(f"Price: ${price:,.2f}") # 千位分隔符
print(f"Hex: {255:#06x}") # 十六进制,宽度 6,前导 0
print(f"Binary: {42:08b}") # 二进制,宽度 8,前导 0标准库里的 itertools 提供了不少实用的迭代工具,排列组合、无限计数都有:
importitertools
counter = itertools.count(start=10, step=2)
print(next(counter)) # 10
print(next(counter)) # 12
print(next(counter)) # 14
letters = ['A', 'B', 'C']
combinations = list(itertools.combinations(letters, 2))
print(combinations) # [('A', 'B'), ('A', 'C'), ('B', 'C')]
permutations = list(itertools.permutations(letters, 2))
print(permutations) # [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]用 @contextmanager 装饰器可以把普通函数变成上下文管理器,不需要写 __enter__/__exit__:
fromcontextlibimportcontextmanager
importtime
@contextmanager
deftimer(label: str):
start = time.time()
try:
yield
finally:
end = time.time()
print(f"{label}: {end - start:.4f} seconds")
withtimer("Calculation"):
result = sum(i**2foriinrange(1000000))
print(f"Result: {result}")主要用来存数据的类,用 @dataclass 比手写 __init__/__repr__/__eq__ 省很多:
fromdataclassesimportdataclass, field
fromtypingimportList
fromdatetimeimportdatetime
@dataclass(order=True)
classProduct:
name: str
price: float
category: str = "General"
tags: List[str] = field(default_factory=list)
created_at: datetime = field(default_factory=datetime.now)
defdisplay_price(self) ->str:
returnf"${self.price:.2f}"
product1 = Product("Laptop", 999.99, "Electronics", ["tech", "portable"])
product2 = Product("Book", 29.99, "Education")
print(product1)
print(f"Formatted price: {product1.display_price()}")
print(f"product1 > product2: {product1 > product2}")functools.partial 可以预先固定函数的某些参数,生成一个新的可调用对象:
fromfunctoolsimportpartial
defpower(base: float, exponent: float) ->float:
returnbase**exponent
square = partial(power, exponent=2)
cube = partial(power, exponent=3)
print(f"Square of 5: {square(5)}") # 25.0
print(f"Cube of 3: {cube(3)}") # 27.0对于重复计算成本高的函数,加一个 @lru_cache 就能自动缓存结果:
fromfunctoolsimportlru_cache
importtime
@lru_cache(maxsize=128)
deffibonacci(n: int) ->int:
ifn<2:
returnn
returnfibonacci(n-1) +fibonacci(n-2)
start = time.time()
result = fibonacci(35)
print(f"fibonacci(35) = {result}")
print(f"Time taken: {time.time() - start:.4f} seconds")
start = time.time()
result = fibonacci(35)
print(f"Cached call time: {time.time() - start:.6f} seconds")比普通元组多字段名,比 dataclass 更轻量,适合只读数据结构:
fromtypingimportNamedTuple
classPoint3D(NamedTuple):
x: float
y: float
z: float
defdistance_from_origin(self) ->float:
return (self.x**2+self.y**2+self.z**2) **0.5
p1 = Point3D(1.0, 2.0, 3.0)
p2 = Point3D(4.0, 5.0, 6.0)
print(f"Point 1: {p1}")
print(f"Distance from origin: {p1.distance_from_origin():.2f}")
print(f"Points are equal: {p1 == p2}")有多层配置(默认值、用户配置、环境变量)时,ChainMap 比手动合并字典更清晰,优先级也更好控制:
fromcollectionsimportChainMap
default_config = {"theme": "light", "language": "en", "debug": False}
user_config = {"theme": "dark", "font_size": 14}
env_config = {"debug": True}
# 优先级:env_config > user_config > default_config
config = ChainMap(env_config, user_config, default_config)
print(f"Theme: {config['theme']}") # 来自 user_config
print(f"Language: {config['language']}") # 来自 default_config
print(f"Debug: {config['debug']}") # 来自 env_config:= 在 for 循环里结合 re.match 特别好用,省掉了先 match 再判断 if match 的步骤:
importre
lines = [
"Error: File not found",
"Warning: Low disk space",
"Info: Process started",
"Error: Permission denied",
]
errors = []
forlineinlines:
ifmatch := re.match(r"Error: (.+)", line):
errors.append(match.group(1))
print(f"Errors found: {errors}")
# 列表推导式里也能用
data = [1, 2, 3, 4, 5]
processed = [n_squaredforxindataif (n_squared := x**2) >10]
print(f"Squares > 10: {processed}")Python 3.5+ 的扩展解包,合并列表、字典都很方便:
first, *middle, last = [1, 2, 3, 4, 5]
print(f"First: {first}, Middle: {middle}, Last: {last}")
# 合并字典(Python 3.9+)
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
merged = dict1|dict2
print(f"Merged dict: {merged}")
# 函数参数解包
defconnect(host, port, timeout=30):
print(f"Connecting to {host}:{port} (timeout: {timeout})")
server_config = {"host": "example.com", "port": 8080, "timeout": 60}
connect(**server_config)
# 合并列表
list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = [*list1, *list2]
print(f"Combined list: {combined}")__slots__ 减少内存占用需要创建大量实例的类,加 __slots__ 可以显著减少每个实例的内存开销:
classPlayer:
__slots__ = ('name', 'score', 'level')
def__init__(self, name: str, score: int = 0, level: int = 1):
self.name = name
self.score = score
self.level = level
deflevel_up(self):
self.level += 1
self.score += 100
players = [Player(f"Player{i}", i*100, i//10+1) foriinrange(1000)]
importsys
player_instance = Player("Test")
print(f"Memory size of Player instance: {sys.getsizeof(player_instance)} bytes")用 @property 可以把方法包装成属性访问,在赋值时加验证逻辑也很自然:
classCircle:
def__init__(self, radius: float):
self._radius = radius
@property
defradius(self) ->float:
returnself._radius
@radius.setter
defradius(self, value: float):
ifvalue<= 0:
raiseValueError("Radius must be positive")
self._radius = value
@property
defdiameter(self) ->float:
return2*self._radius
@diameter.setter
defdiameter(self, value: float):
self._radius = value/2
@property
defarea(self) ->float:
return3.14159*self._radius**2
circle = Circle(5.0)
print(f"Radius: {circle.radius}")
print(f"Area: {circle.area:.2f}")
circle.diameter = 20.0
print(f"New radius after setting diameter: {circle.radius}")@staticmethod 不依赖实例或类,纯粹是挂在类名下的工具函数;@classmethod 拿到的是类本身,常用作替代构造函数:
classTemperatureConverter:
@staticmethod
defcelsius_to_fahrenheit(celsius: float) ->float:
returncelsius*9/5+32
@staticmethod
deffahrenheit_to_celsius(fahrenheit: float) ->float:
return (fahrenheit-32) *5/9
@classmethod
deffrom_string(cls, temp_str: str):
value, unit = temp_str.split()
value = float(value)
ifunit.upper() == 'C':
returncls(value, 'C')
elifunit.upper() == 'F':
celsius = cls.fahrenheit_to_celsius(value)
returncls(celsius, 'C')
else:
raiseValueError(f"Unknown unit: {unit}")
def__init__(self, value: float, unit: str = 'C'):
self.celsius = valueifunit.upper() == 'C'elseself.fahrenheit_to_celsius(value)
@property
deffahrenheit(self) ->float:
returnself.celsius_to_fahrenheit(self.celsius)
def__str__(self):
returnf"{self.celsius:.1f}°C ({self.fahrenheit:.1f}°F)"
print(f"37°C in Fahrenheit: {TemperatureConverter.celsius_to_fahrenheit(37):.1f}")
temp1 = TemperatureConverter(25, 'C')
print(f"Temperature 1: {temp1}")
temp2 = TemperatureConverter.from_string("98.6 F")
print(f"Temperature 2: {temp2}")用 ABC 定义接口,强制子类实现指定方法,防止漏掉:
fromabcimportABC, abstractmethod
fromtypingimportList
classDataProcessor(ABC):
@abstractmethod
defload_data(self, source: str) ->List[dict]:
pass
@abstractmethod
defprocess(self, data: List[dict]) ->List[dict]:
pass
@abstractmethod
defsave_result(self, data: List[dict], destination: str) ->bool:
pass
defrun_pipeline(self, source: str, destination: str) ->bool:
try:
data = self.load_data(source)
processed_data = self.process(data)
returnself.save_result(processed_data, destination)
exceptExceptionase:
print(f"Pipeline failed: {e}")
returnFalse
classCSVProcessor(DataProcessor):
defload_data(self, source: str) ->List[dict]:
print(f"Loading CSV data from {source}")
return [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
defprocess(self, data: List[dict]) ->List[dict]:
importdatetime
foritemindata:
item["processed_at"] = datetime.datetime.now().isoformat()
returndata
defsave_result(self, data: List[dict], destination: str) ->bool:
print(f"Saving processed data to {destination}")
returnTrue
processor = CSVProcessor()
success = processor.run_pipeline("input.csv", "output.csv")
print(f"Pipeline completed: {success}")I/O 密集型任务(比如同时请求多个接口)用异步可以大幅提升吞吐:
importasyncio
importaiohttp
importtime
fromtypingimportList
asyncdeffetch_url(session: aiohttp.ClientSession, url: str) ->str:
asyncwithsession.get(url) asresponse:
returnawaitresponse.text()
asyncdeffetch_multiple_urls(urls: List[str]):
asyncwithaiohttp.ClientSession() assession:
tasks = [fetch_url(session, url) forurlinurls]
results = awaitasyncio.gather(*tasks, return_exceptions=True)
returnresults
asyncdefmain():
urls = [
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/2",
"https://httpbin.org/delay/1",
]
start_time = time.time()
results = awaitfetch_multiple_urls(urls)
print(f"Fetched {len(urls)} URLs in {time.time() - start_time:.2f} seconds")
fori, resultinenumerate(results):
ifisinstance(result, Exception):
print(f"URL {i+1} failed: {result}")
else:
print(f"URL {i+1} response length: {len(result)}")
# asyncio.run(main())
print("Note: Async code requires asyncio.run() to execute")描述符可以把验证逻辑抽出来复用,不用在每个属性的 setter 里重复写:
classValidatedAttribute:
def__init__(self, min_value=None, max_value=None, allowed_values=None):
self.min_value = min_value
self.max_value = max_value
self.allowed_values = allowed_values
self.name = None
def__set_name__(self, owner, name):
self.name = name
def__get__(self, obj, objtype=None):
ifobjisNone:
returnself
returnobj.__dict__.get(self.name)
def__set__(self, obj, value):
self._validate(value)
obj.__dict__[self.name] = value
def_validate(self, value):
ifself.min_valueisnotNoneandvalue<self.min_value:
raiseValueError(f"{self.name} must be >= {self.min_value}")
ifself.max_valueisnotNoneandvalue>self.max_value:
raiseValueError(f"{self.name} must be <= {self.max_value}")
ifself.allowed_valuesisnotNoneandvaluenotinself.allowed_values:
raiseValueError(f"{self.name} must be one of {self.allowed_values}")
classProduct:
price = ValidatedAttribute(min_value=0)
quantity = ValidatedAttribute(min_value=0, max_value=1000)
category = ValidatedAttribute(allowed_values=["Electronics", "Books", "Clothing"])
def__init__(self, name: str, price: float, quantity: int, category: str):
self.name = name
self.price = price
self.quantity = quantity
self.category = category
@property
deftotal_value(self) ->float:
returnself.price*self.quantity
try:
laptop = Product("Laptop", 999.99, 10, "Electronics")
print(f"Total value: ${laptop.total_value:.2f}")
laptop.price = -100 # 触发 ValueError
exceptValueErrorase:
print(f"Validation error: {e}")
try:
invalid_product = Product("Test", 50, 2000, "Unknown")
exceptValueErrorase:
print(f"Validation error: {e}")元类是 Python 里比较深的特性,最常见的实际用途之一是实现单例:
classSingletonMeta(type):
_instances = {}
def__call__(cls, *args, **kwargs):
ifclsnotincls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
returncls._instances[cls]
classDatabaseConnection(metaclass=SingletonMeta):
def__init__(self, connection_string: str):
self.connection_string = connection_string
self._connected = False
print(f"Initializing connection to {connection_string}")
defconnect(self):
ifnotself._connected:
print("Establishing connection...")
self._connected = True
returnself._connected
defexecute_query(self, query: str):
ifnotself._connected:
raiseRuntimeError("Not connected to database")
returnf"Result of: {query}"
db1 = DatabaseConnection("mysql://localhost:3306/mydb")
db1.connect()
db2 = DatabaseConnection("mysql://localhost:3306/mydb") # 不会重新初始化
print(f"db1 is db2: {db1 is db2}") # True
print(db1.execute_query("SELECT * FROM users"))
print(db2.execute_query("SELECT * FROM products"))装饰器可以叠加使用,下面是两个实用的例子——计时和自动重试:
importtime
importfunctools
fromtypingimportCallable, Any
defretry(max_attempts: int = 3, delay: float = 1.0):
defdecorator(func: Callable) ->Callable:
@functools.wraps(func)
defwrapper(*args, **kwargs) ->Any:
last_exception = None
forattemptinrange(1, max_attempts+1):
try:
print(f"Attempt {attempt}/{max_attempts}...")
returnfunc(*args, **kwargs)
exceptExceptionase:
last_exception = e
ifattempt<max_attempts:
print(f"Failed: {e}. Retrying in {delay}s...")
time.sleep(delay)
raiselast_exception
returnwrapper
returndecorator
deftimer(func: Callable) ->Callable:
@functools.wraps(func)
defwrapper(*args, **kwargs) ->Any:
start_time = time.time()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.time() - start_time:.4f} seconds")
returnresult
returnwrapper
@timer
@retry(max_attempts=2, delay=0.5)
defunstable_operation(should_fail: bool = True):
ifshould_fail:
raiseRuntimeError("Operation failed!")
return"Success"
try:
result = unstable_operation(should_fail=True)
exceptExceptionase:
print(f"Operation failed with: {e}")标记废弃 API 或者给用户提示非致命问题,用 warnings 比直接 print 更规范——调用方可以选择过滤或捕获:
importwarnings
importdatetime
defprocess_data(data, use_old_method=False):
ifuse_old_method:
warnings.warn(
"Old method is deprecated and will be removed in v2.0. "
"Use the default method instead.",
DeprecationWarning,
stacklevel=2
)
returnf"Old result: {len(data)}"
returnf"New result: {sum(data)}"
defcheck_expiry(expiry_date):
today = datetime.date.today()
ifexpiry_date<today:
warnings.warn(f"Product expired on {expiry_date}", UserWarning)
returnFalse
elif (expiry_date-today).days<= 7:
warnings.warn(
f"Product will expire in {(expiry_date - today).days} days",
UserWarning
)
returnTrue
warnings.simplefilter("always")
result1 = process_data([1, 2, 3], use_old_method=True)
print(f"Result: {result1}")
today = datetime.date.today()
check_expiry(today-datetime.timedelta(days=30))
check_expiry(today+datetime.timedelta(days=3))
check_expiry(today+datetime.timedelta(days=100))
# 忽略 DeprecationWarning
warnings.simplefilter("ignore", category=DeprecationWarning)
result2 = process_data([1, 2, 3], use_old_method=True)
print(f"Result (no warning): {result2}")调试阶段用 print 没问题,但正式项目里 logging 能分级别、写文件、按大小轮转,维护起来方便得多:
importlogging
importlogging.handlers
frompathlibimportPath
defsetup_logging(
name: str,
log_file: str = "app.log",
console_level=logging.INFO,
file_level=logging.DEBUG
):
log_path = Path(log_file)
log_path.parent.mkdir(parents=True, exist_ok=True)
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
iflogger.handlers:
returnlogger
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
)
console_handler = logging.StreamHandler()
console_handler.setLevel(console_level)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
file_handler = logging.handlers.RotatingFileHandler(
log_file, maxBytes=10*1024*1024, backupCount=5
)
file_handler.setLevel(file_level)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
returnlogger
classDataProcessor:
def__init__(self, name: str):
self.logger = setup_logging(f"DataProcessor.{name}")
self.name = name
self.logger.info(f"DataProcessor '{name}' initialized")
defprocess(self, data):
self.logger.debug(f"Starting to process {len(data)} items")
ifnotdata:
self.logger.warning("Empty data provided")
return []
try:
result = []
fori, iteminenumerate(data):
processed = self._transform(item)
result.append(processed)
if (i+1) %10 == 0:
self.logger.info(f"Processed {i + 1}/{len(data)} items")
self.logger.info(f"Successfully processed {len(data)} items")
returnresult
exceptExceptionase:
self.logger.error(f"Error processing data: {e}", exc_info=True)
raise
def_transform(self, item):
ifnotisinstance(item, (int, float)):
self.logger.warning(f"Unexpected type: {type(item)}")
ifitem == 0:
self.logger.error("Cannot process zero value")
raiseValueError("Zero value not allowed")
returnitem*2
processor = DataProcessor("TestProcessor")
test_data = list(range(1, 25))
test_data.append(0)
try:
result = processor.process(test_data)
print(f"Result length: {len(result)}")
exceptExceptionase:
print(f"Processing failed: {e}")
logger = setup_logging("Demo")
logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")以上 30 条覆盖了日常开发里比较常踩到的点。随时能用上在写库或者需要长期维护的项目里更有价值。不需要全部记住,遇到对应场景时翻出来用就行。
“无他,惟手熟尔”!有需要的用起来!
如果你觉得这篇文章有用,欢迎点赞、转发、收藏、留言、推荐❤!
本文分享自 Nicholas与Pypi 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!