写在前面
Python 隐藏特性探秘会介绍一些不被大众所熟知的代码特性。在这篇文章中我介绍一些编码的技巧和Python的特性,希望能够在你的日常编码中帮助到你。
让我们开始吧——
1、函数对象
Python 中的所有东西都是某个类的实例化对象,当然也包括函数,但是这一事实可能让你感觉有悖直觉,别着急,让我们看一个例子。
def add_subject(name, subject, subjects=[]):
subjects.append(subject)
return {'name': name, 'subjects': subjects}
add_subject('person1', 'subject1')
add_subject('person2', 'subject2')
add_subject('person3', 'subject3')
输出 -
{'name': 'person1', 'subjects': ['subject1']}
{'name': 'person2', 'subjects': ['subject1', 'subject2']}
{'name': 'person3', 'subjects': ['subject1', 'subject2', 'subject3']}
是不是和你预想的结果不太一样,你是否觉得 person2 和 person3 的 subjects 多了一些东西。别着急,继续往下看。
Python中的可变性可能是最被误解和忽视的概念之一。
函数的默认参数在定义函数时就进行了初始化。因此,一旦定义了一个函数,函数对象就会在其__defaults__
属性中存储默认参数。
下面我们通过代码来进行验证
def my_functions(a=1, b=2, c=3):
pass
print(my_functions.defaults)
输出
(1, 2, 3)
那么,如果在函数定义中指定了一个可变的默认参数呢,您就不知不觉中改变了该函数所有调用的参数。
下面的代码显示了这一点。Python 没有在每次函数调用时创建一个新列表,而是向同一副本追加元素。
def add_subject(name, subject, subjects=[]):
subjects.append(subject)
return {'name': name, 'subjects': subjects}
print(add_subject.defaults)
add_subject('person1', 'subject1')
print(add_subject.defaults)
add_subject('person2', 'subject2')
print(add_subject.defaults)
add_subject('person3', 'subject3'))
print(add_subject.defaults)
输出 -
([],)
(['subject1'],)
(['subject1', 'subject2'],)
(['subject1', 'subject2', 'subject3'],)
如何避免这个问题呢?
不要在函数定义中指定可变的默认参数(例如列表),而是用 None 来替换它们。如果在函数调用期间函数没有收到相应的值,请在函数内部创建可变对象。
def add_subject(name, subject, subjects=None):
if subjects is None:
subjects = []
subjects.append(subject)
return {'name': name, 'subjects': subjects}
print(add_subject.defaults)
print(add_subject('person1', 'subject1'))
print(add_subject('person2', 'subject2'))
print(add_subject('person3', 'subject3'))
输出 -
(None,)
{'name': 'person1', 'subjects': ['subject1']}
{'name': 'person2', 'subjects': ['subject2']}
{'name': 'person3', 'subjects': ['subject3']}
如上所示,如果函数在被调用时没有收到任何值,我们会创建一个新的列表。这可以避免意外地改变同一个对象。
2、让类对象像函数一样可调用
如果你想让你一个类可以像函数一样可调用,可以通过定义__call__
方法来实现。该方法将会使你的类像函数一样可调用。
class Multiplier:
def init(self, a, b, c):
self.a = a
self.b = b
self.c = c
def __call__(self, x):
return (self.a * x**2) + (self.b * x**2) + (self.c * x**2)
func = Multiplier(2, 4, 6)
print(func(2)) # 48
print(func(4)) # 192
print(callable(func)) # True
以上代码中,我们直接像使用函数一样使用 Multiplier 类。那么这样做会带来什么好处呢?
-
我们实现可以以灵活、直观的方式使用的对象 -
熟悉的函数调用语法有时可以使代码更具可读性 -
它允许您在需要可调用对象的上下文中使用类对象。例如将类用作装饰器。
3、字符串方法
这里有一些 Python 字符串方法的新信息和很酷的地方。在 Python 中,startswith 和 endswith 字符串方法通常用于在字符串开头和结尾匹配子字符串。
places = ["India", "London", "Poland", "Netherlands"]
for place in places:
if place.startswith('Lo') or place.startswith('Po'):
print(place)
然而,你知道可以将多个子字符串作为元组传递给这些方法吗?
places = ["India", "London", "Poland", "Netherlands"]
for place in places:
if place.startswith(('Lo', 'Po')):
print(place)
这大大简化了你的条件代码,同时保持相同的功能。
4、使用 Frozenset
Python 中的字典要求其键是不可变的。由于集合是可变的,所以不能将集合用作键。
set_ex = {1, 2, 3}
dict_obj = {set_ex: "This is a Set"}
# Error - TypeError: unhashable type: 'set'
如果要使用集合,可以将其声明为不可变集合。
set_ex = frozenset({1, 2, 3})
dict_obj = {set_ex: "This is a Set"}
print(dict_obj[set_ex])
输出 - This is a Set
它是一个不可变的集合,意味着它的元素在创建后不能更改。因此,它们可以安全地用作字典的键。
5、Pickle 文件
Pickle 文件广泛用于数据对象持久化,即将数据对象存储到硬盘上。但是人们经常只将单个对象转储到文件中。为了存储多个对象,会创建多个文件。
你可能不知道我们可以在单个 Pickle 文件中存储很多对象。此外,在加载时,不需要加载所有对象。只需确保转储对象在同一上下文管理器(使用with)中。
import pickle
a, b, c = 1, 2, 3
with open("data.pkl", "wb") as f:
pickle.dump(a, f)
pickle.dump(b, f)
pickle.dump(c, f)
这段代码会在当前目录创建文件data.pkl
并存储a、b、c的值。
import pickle
with open("data.pkl", "rb") as f:
a = pickle.load(f)
b = pickle.load(f)
print(f"{a = } {b = }")
结果:
a = 1 b = 2
当然,还有一种解决方案是将对象组合在一起作为元组。但在重新加载时,会加载整个元组。在某些情况下,这可能不是所需的。
总结
以上内容介绍了Python的一些隐藏特性。其中包括函数对象的特性,如默认参数的初始化和避免可变默认参数带来的问题;还有让类对象像函数一样可调用的方法;以及字符串方法的一些使用技巧。这些特性可以提高代码的灵活性和可读性,持续关注 harvey 的网络日志公众号,让我们在编程过程中更加高效和方便。
原文始发于微信公众号(harvey的网络日志):探秘 :你不了解的 Python 隐藏特性
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/203521.html