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')
output -
{'name': 'person1', 'subjects': ['subject1']}
{'name': 'person2', 'subjects': ['subject1', 'subject2']}
{'name': 'person3', 'subjects': ['subject1', 'subject2', 'subject3']}
Python 中的可变性可能是最容易被误解和忽视的概念之一。
函数的默认参数在定义函数时立即计算。因此,一旦定义了函数,函数对象就会将默认参数存储在其 __defaults__ 属性中。
可以在下面验证这一点——
def my_functions(a=1, b=2, c=3):
pass
print(my_functions.__defaults__)
output - (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__)
Output -
([],)
(['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', 'subject1'))
print(add_subject('person3', 'subject1'))
## Output -
(None,)
{'name': 'person1', 'subjects': ['subject1']}
{'name': 'person2', 'subjects': ['subject1']}
{'name': 'person3', 'subjects': ['subject1']}
如上所示,如果函数在调用时未收到任何值,将创建一个新列表。这样可以避免发生更改同一对象的意外行为。
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
这可能有很多优点。例如,它允许我们实现可以灵活直观地使用的对象。更重要的是,熟悉的函数调用语法有时可以使代码更具可读性。
它允许在需要可调用对象的上下文中使用类对象。例如,使用类作为装饰器。
3. 字符串法
在 Python 中, startswith 和 endswith string 方法通常用于匹配字符串开头和结尾的子字符串。
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'
如果要使用集合,请考虑将其声明为 FrozenSet。
set_ex = frozenset({1, 2, 3})
dict_obj = {set_ex: "This is a Set"}
print(dict_obj[set_ex])
## Output - This is a Set
它是一个不可变的集合,这意味着它的元素在创建后无法更改。因此,它们可以安全地用作字典的键。
5. pickle文件
pickle广泛用于将数据对象转储到磁盘。但是人们通常只将单个对象转储到pickle文件中。此外,一个人创建多个pickle来存储多个对象。但是,可以在单个pickle文件中存储任意数量的对象?更重要的是,重新加载时,没有必要加载所有对象。
只需确保将对象转储到同一上下文管理器中(与一起使用)。
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)
import pickle
with open("data.pkl", "rb") as f:
a = pickle.load(f)
b = pickle.load(f)
print(f"{a = } {b = }")
当然,一种解决方案是将对象存储为元组。但是在重新加载时,将加载整个元组。在某些情况下,这可能不是需要的。