Python 技巧 - 黑魔法 - 第一部分
介绍
编者注:本指南是实用 Python 技巧系列的一部分。阅读有关本系列的更多信息并在此处找到其他指南的链接。
在本指南中,我们将开始学习一些不常见的 Python 高级技巧。这些技巧包括一些我称之为“黑魔法”的神奇技巧。每个 Python 技巧都分两个步骤进行解释:
- 概念的简要描述和交互式代码示例
- 一些鼓舞人心的例子来启发你
黑魔法的解释
本指南涵盖了一些不常见的技巧,其中很多技巧可能会产生副作用。但这些技巧真的很酷,在特定场景中发挥着神奇的作用——所以我称它们为 Python 世界中的“黑魔法”。由于场景限制和副作用,它们的主要作用是打开你的思路,点亮你的灵感,提升你的 Python 能力。
一旦您权衡了利弊并知道如何正确使用它们,其中一些可能会成为您代码的一部分。
东亚足联
这篇文章《惯用的 Python:EAFP 与 LBYL》很好地阐述了 EAFP 与 LBYL 的区别:
EAFP(请求原谅比请求许可更容易)意味着您应该只做您期望做的事情,如果操作可能引发异常,那么捕获它并处理这个事实。
LBYL(三思而后行)是指首先检查某件事是否会成功,并且只有确定它会成功时才继续进行。
"""LBYL"""
if "key" in dict_:
value += dict_["key"]
"""EAFP"""
# downplays the importance of the key
try:
value += dict_["key"]
except KeyError:
pass
这遵循了 Python 的设计理念:“显式优于隐式”。
EAFP 在 Python 中的表现也是可以接受的:
但是由于异常用于控制流(如在 EAFP 中),Python 实现会努力使异常成为一种廉价的操作,因此您在编写代码时不必担心异常的成本。
与其他黑魔法不同,这是 Python 中推荐的编码风格。
EAFP 是Python 中合法的编码风格,当它有意义时你可以随意使用它。
让我们用 Pythonic 风格编写一个斐波那契示例。我们将使用生成器提供结果并以 EAFP 风格进行迭代。
"""pythonic fibonacci"""
# generator function
def fibonacci(n):
a, b, counter = 0, 1, 0
while counter <= n:
yield a
a, b = b, a + b # multiple assignment
counter += 1
f = fibonacci(5)
# EAFP
while True:
try:
print (next(f), end=" ")
except StopIteration:
break
# output: 0 1 1 2 3 5
鼓舞人心的例子
"""used in detect cycle in linked list"""
# L141: determine there is a cycle in a linked list
def has_cycle_in_linked_list(head: ListNode) -> bool:
try:
slow = head
fast = head.next
while slow is not fast:
slow = slow.next
fast = fast.next.next
return True
except:
return False
"""used in binary search and insert"""
# L334: given an unsorted array return whether an increasing subsequence (incontinuous) of length k exists or not in the array.
def increasing_triplet(nums: List[int], k: int) -> bool:
try:
inc = [float('inf')] * (k - 1)
for x in nums:
inc[bisect.bisect_left(inc, x)] = x
return k == 0
except:
return True
"""used in eval"""
# L301: Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results.
def remove_invalid_parentheses(s: str) -> List[str]:
def isvalid(s):
try:
eval('0,' + ''.join(filter('()'.count, s)).replace(')', '),'))
return True
except:
pass
level = {s}
while True:
valid = list(filter(isvalid, level))
if valid:
return valid
level = {s[:i] + s[i+1:] for s in level for i in range(len(s))}
自助
这是动态属性的一个技巧。成本有点高,因为它会污染原始结构。
鼓舞人心的例子
使用自身作为链接列表中的虚拟节点。
"""reuse self to save the cost of dummy node"""
def traverse_linked_list(self, head: TreeNode) -> TreeNode:
pre, pre.next = self, head
while pre.next:
self.process_logic(pre.next)
return self.next # return head node
评估
eval()将任意字符串作为 Python 代码执行。但请注意,eval可能会成为潜在的安全风险。
鼓舞人心的例子
构建树构造函数并使用eval执行。
# L536: construct binary tree from string
def str2tree(s: str) -> TreeNode:
def t(val, left=None, right=None):
node, node.left, node.right = TreeNode(val), left, right
return node
return eval('t(' + s.replace('(', ',t(') + ')') if s else None
str.查找
这是str.find的一个巧妙用法,虽然这个用法不太具有普遍性,但是思路还是可以借鉴的。
鼓舞人心的例子
我们使用find来构建一种机制:如果val是 '#' 则返回 1,如果不是则返回 -1。
# L331: verify preorder serialization of a binary tree, if it is a null node, we record using a sentinel value #
def is_valid_serialization(preorder: str) -> bool:
need = 1
for val in preorder.split(','):
if not need:
return False
need -= ' #'.find(val)
return not need
字符串替换
使用str.replace实现字符串版本的并查集(不相交集)。
鼓舞人心的例子
与普通的 union-find 相比,这个字符串版本简洁,优雅,高效。
"""convert int to unicode char, and use str.replace to merge the connected nodes by reduce"""
# L323: given n nodes from 0 to n - 1 and a list of undirected edges, find the number of connected components in an undirected graph.
def count_connected_components(n: int, edges: List[List[int]]) -> int:
return len(set(reduce(lambda s, edge: s.replace(s[edge[1]], s[edge[0]]), edges, ''.join(map(chr, range(n))))))
^ (异或)
^通常用于删除完全相同的数字并保留奇数,或者保留不同的位并删除相同的位。
鼓舞人心的例子
这些例子说明了^的一些令人惊奇的用法。
"""Use ^ to remove even exactly same numbers and save the odd, or save the distinct bits and remove the same."""
"""bit manipulate: a^b^b = a"""
# L268: Given an array containing n distinct numbers taken from 0, 1, 2, ..., n, find the one that is missing from the array.
def missing_number(nums: List[int]) -> int:
res = 0
for i, e in enumerate(nums):
res = res ^ i ^ e
return res ^ len(nums)
"""simply find the first index whose "partner index" (the index xor 1) holds a different value."""
# L540: find the single element, in a sorted array where every element appears twice except for one.
def single_non_duplicate(nums: List[int]) -> int:
lo, hi = 0, len(nums) - 1
while lo < hi:
mid = (lo + hi) // 2
if nums[mid] == nums[mid ^ 1]:
lo = mid + 1
else:
hi = mid
return nums[lo]
"""parity in triple comparisons"""
# L81: search a target in a rotated sorted array
def search_in_rotated_sorted_arr(nums: List[int], target: int) -> int:
"""I have three checks (nums[0] <= target), (target <= nums[i]) and (nums[i] < nums[0]), and I want to know
whether exactly two of them are true. They can't all be true or all be false (check it), so I just need to
distinguish between "two true" and "one true". Parity is enough for that, so instead of adding them I xor them"""
self.__getitem__ = lambda i: (nums[0] <= target) ^ (nums[0] > nums[i]) ^ (target > nums[i])
i = bisect.bisect_left(self, True, 0, len(nums))
return i if target in nums[i:i+1] else -1
结论
在本指南中,我们学习了一些 Python 黑魔法技巧,例如 EAFP、eval、xor 以及一些内置函数的高级技巧。我希望其中一些对你有用。
在第 2 部分中,我们将继续学习其他 Python 黑魔法技巧。您还可以从 Github下载示例笔记本black_magics.ipynb 。
本指南是 Python 技巧指南系列之一:
- Python 技巧 - 简介
- Python 技巧 - 基础 - 第一部分
- Python 技巧 - 基础 - 第二部分
- Python 技巧 - Iterable - 第一部分
- Python 技巧 - Iterable - 第二部分
- Python 技巧 - 黑魔法 - 第一部分
- Python 技巧 - 黑魔法 - 第 2 部分
希望你喜欢。如果您有任何问题,欢迎通过 recnac@foxmail.com 与我联系。
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~