你可能不知道的Python 技巧小结
|
译者 | 豌豆花下猫 声明 :本文获得原作者授权翻译,转载请保留原文出处,请勿用于商业或非法用途。 有许许多多文章写了 Python 中的许多很酷的特性,例如变量解包、偏函数、枚举可迭代对象,但是关于 Python 还有很多要讨论的话题,因此在本文中,我将尝试展示一些我知道的和在使用的,但很少在其它文章提到过的特性。那就开始吧。 1、对输入的字符串“消毒” 对用户输入的内容“消毒”,这问题几乎适用于你编写的所有程序。通常将字符转换为小写或大写就足够了,有时你还可以使用正则表达式来完成工作,但是对于复杂的情况,还有更好的方法:
user_input = "Thisnstring hastsome whitespaces...rn"
character_map = {
ord('n') : ' ',ord('t') : ' ',ord('r') : None
}
user_input.translate(character_map) # This string has some whitespaces... "
在此示例中,你可以看到空格字符“ n”和“ t”被单个空格替换了,而“ r”则被完全删除。这是一个简单的示例,但是我们可以更进一步,使用 2、对迭代器切片 如果你尝试直接对迭代器切片,则会得到 TypeError ,提示说该对象不可取下标(not subscriptable),但是有一个简单的解决方案: import itertools s = itertools.islice(range(50),10,20) # <itertools.islice object at 0x7f70fab88138> for val in s: ... 使用 (译注:更多关于迭代器切片的内容,可阅读 Python进阶:迭代器与迭代器切片) 3、跳过可迭代对象的开始 有时候你必须处理某些文件,它们以可变数量的不需要的行(例如注释)为开头。 itertools 再次提供了简单的解决方案:
string_from_file = """
// Author: ...
// License: ...
//
// Date: ...
Actual content...
"""
import itertools
for line in itertools.dropwhile(lambda line:line.startswith("//"),string_from_file.split("n")):
print(line)
这段代码仅会打印在初始的注释部分之后的内容。如果我们只想丢弃迭代器的开头部分(在此例中是注释),并且不知道有多少内容,那么此方法很有用。 4、仅支持关键字参数(kwargs)的函数 当需要函数提供(强制)更清晰的参数时,创建仅支持关键字参数的函数,可能会挺有用:
def test(*,a,b):
pass
test("value for a","value for b") # TypeError: test() takes 0 positional arguments...
test(a="value",b="value 2") # Works...
如你所见,可以在关键字参数之前,放置单个 * 参数来轻松解决此问题。如果我们将位置参数放在 * 参数之前,则显然也可以有位置参数。 5、创建支持 with 语句的对象 我们都知道如何使用 with 语句,例如打开文件或者是获取锁,但是我们可以实现自己的么?是的,我们可以使用__enter__ 和__exit__ 方法来实现上下文管理器协议: class Connection: def __init__(self): ... def __enter__(self): # Initialize connection... def __exit__(self,type,value,traceback): # Close connection... with Connection() as c: # __enter__() executes ... # conn.__exit__() executes 这是在 Python 中实现上下文管理的最常见方法,但是还有一种更简单的方法:
from contextlib import contextmanager
@contextmanager
def tag(name):
print(f"<{name}>")
yield
print(f"</{name}>")
with tag("h1"):
print("This is Title.")
上面的代码段使用 contextmanager 装饰器实现了内容管理协议。tag 函数的第一部分(yield 之前)会在进入 with 语句时执行,然后执行 with 的代码块,最后会执行 tag 函数的剩余部分。 6、用__slots__节省内存 如果你曾经编写过一个程序,该程序创建了某个类的大量实例,那么你可能已经注意到你的程序突然就需要大量内存。那是因为 Python 使用字典来表示类实例的属性,这能使其速度变快,但内存不是很高效。通常这不是个问题,但是,如果你的程序遇到了问题,你可以尝试使用__slots__ : class Person: __slots__ = ["first_name","last_name","phone"] def __init__(self,first_name,last_name,phone): self.first_name = first_name self.last_name = last_name self.phone = phone 这里发生的是,当我们定义__slots__属性时,Python 使用固定大小的小型数组,而不是字典,这大大减少了每个实例所需的内存。使用__slots__还有一些缺点——我们无法声明任何新的属性,并且只能使用在__slots__中的属性。同样,带有__slots__的类不能使用多重继承。 7、限制CPU和内存使用量 如果不是想优化程序内存或 CPU 使用率,而是想直接将其限制为某个固定数字,那么 Python 也有一个库能做到:
import signal
import resource
import os
# To Limit CPU time
def time_exceeded(signo,frame):
print("CPU exceeded...")
raise SystemExit(1)
def set_max_runtime(seconds):
# Install the signal handler and set a resource limit
soft,hard = resource.getrlimit(resource.RLIMIT_CPU)
resource.setrlimit(resource.RLIMIT_CPU,(seconds,hard))
signal.signal(signal.SIGXCPU,time_exceeded)
# To limit memory usage
def set_max_memory(size):
soft,hard = resource.getrlimit(resource.RLIMIT_AS)
resource.setrlimit(resource.RLIMIT_AS,(size,hard))
在这里,我们可以看到两个选项,可设置最大 CPU 运行时间和内存使用上限。对于 CPU 限制,我们首先获取该特定资源(RLIMIT_CPU)的软限制和硬限制,然后通过参数指定的秒数和先前获取的硬限制来设置它。最后,如果超过 CPU 时间,我们将注册令系统退出的信号。至于内存,我们再次获取软限制和硬限制,并使用带有 size 参数的setrlimit 和获取的硬限制对其进行设置。 8、控制可以import的内容 某些语言具有非常明显的用于导出成员(变量、方法、接口)的机制,例如Golang,它仅导出以大写字母开头的成员。另一方面,在 Python 中,所有内容都会被导出,除非我们使用__all__ : def foo(): pass def bar(): pass __all__ = ["bar"] 使用上面的代码段,我们可以限制from some_module import * 在使用时可以导入的内容。对于以上示例,通配导入时只会导入 bar。此外,我们可以将__all__ 设为空,令其无法导出任何东西,并且在使用通配符方式从此模块中导入时,将引发 AttributeError。 9、比较运算符的简便方法 为一个类实现所有比较运算符可能会很烦人,因为有很多的比较运算符——__lt__、__le__、__gt__ 或__ge__。但是,如果有更简单的方法呢?functools.total_ordering 可救场: from functools import total_ordering @total_ordering class Number: def __init__(self,value): self.value = value def __lt__(self,other): return self.value < other.value def __eq__(self,other): return self.value == other.value print(Number(20) > Number(3)) print(Number(1) < Number(5)) print(Number(15) >= Number(15)) print(Number(10) <= Number(2)) 这到底如何起作用的?total_ordering 装饰器用于简化为我们的类实例实现排序的过程。只需要定义__lt__ 和__eq__,这是最低的要求,装饰器将映射剩余的操作——它为我们填补了空白。 ( 译注: 原作者的文章分为两篇,为了方便读者们阅读,我特将它们整合在一起,以下便是第二篇的内容。) 10、使用slice函数命名切片 使用大量硬编码的索引值会很快搞乱维护性和可读性。一种做法是对所有索引值使用常量,但是我们可以做得更好:
# ID First Name Last Name
line_record = "2 John Smith"
ID = slice(0,8)
FIRST_NAME = slice(9,21)
LAST_NAME = slice(22,27)
name = f"{line_record[FIRST_NAME].strip()} {line_record[LAST_NAME].strip()}"
# name == "John Smith"
在此例中,我们可以避免神秘的索引,方法是先使用 slice 函数命名它们,然后再使用它们。你还可以通过 .start、.stop和 .stop 属性,来了解 slice 对象的更多信息。 11、在运行时提示用户输入密码 许多命令行工具或脚本需要用户名和密码才能操作。因此,如果你碰巧写了这样的程序,你可能会发现 getpass 模块很有用: import getpass user = getpass.getuser() password = getpass.getpass() # Do Stuff... 这个非常简单的包通过提取当前用户的登录名,可以提示用户输入密码。但是须注意,并非每个系统都支持隐藏密码。Python 会尝试警告你,因此切记在命令行中阅读警告信息。 12、查找单词/字符串的相近匹配 现在,关于 Python 标准库中一些晦涩难懂的特性。如果你发现自己需要使用Levenshtein distance 【2】之类的东西,来查找某些输入字符串的相似单词,那么 Python 的 difflib 会为你提供支持。
import difflib
difflib.get_close_matches('appel',['ape','apple','peach','puppy'],n=2)
# returns ['apple','ape']
difflib.get_close_matches 会查找最佳的“足够好”的匹配。在这里,第一个参数与第二个参数匹配。我们还可以提供可选参数 n ,该参数指定要返回的最多匹配结果。另一个可选的关键字参数 cutoff (默认值为 0.6),可以设置字符串匹配得分的阈值。 13、使用IP地址 如果你必须使用 Python 做网络开发,你可能会发现 ipaddress 模块非常有用。一种场景是从 CIDR(无类别域间路由 Classless Inter-Domain Routing)生成一系列 IP 地址:
import ipaddress
net = ipaddress.ip_network('74.125.227.0/29') # Works for IPv6 too
# IPv4Network('74.125.227.0/29')
for addr in net:
print(addr)
# 74.125.227.0
# 74.125.227.1
# 74.125.227.2
# 74.125.227.3
# ...
另一个不错的功能是检查 IP 地址的网络成员资格:
ip = ipaddress.ip_address("74.125.227.3")
ip in net
# True
ip = ipaddress.ip_address("74.125.227.12")
ip in net
# False
(编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
