一:python的规则
1:集合的规则
- 规则: 如果要把一个东西装到集合中去,这个东西必须是可以哈希的。
- 可变类型都是不可哈希的。(列表, 集合,字典),本质就是哈希值不固定。
- 思维1: 对于存在a,不存在b等问题,思维定式就是采用集合思维处理。
- 思维2: 我们可以自定义哈希对象。
案例:
给定两个列表套字典的数据,数据内容是人员(姓名,电话相同,则默认就是同一个人),我们要求是找出存在a列表,但是不存在b列表的人员。
list_a = [
{'first_name': 'ren', 'last_name': 'shanshan', 'phone_num': '12345678'},
{'first_name': 'wang', 'last_name': 'gang', 'phone_num': '12323338'},
{'first_name': 'li', 'last_name': 'ruixue', 'phone_num': '123389278'},
]
list_b = [
{'first_name': 'ren', 'last_name': 'shanshan', 'phone_num': '12345678'},
{'first_name': 'wang', 'last_name': 'gang', 'phone_num': '12323338'},
{'first_name': 'xu', 'last_name': 'daer', 'phone_num': '125545678'},
]
class people(object):
def __init__(self, first_name, last_name, phone_num):
self.first_name = first_name
self.last_name = last_name
self.phone_num = phone_num
def __hash__(self):
# 默认是内存地址的哈希,这里我们改成名字+电话这样的一个元组的哈希值。
return hash(
(self.first_name, self.last_name, self.phone_num)
)
def __eq__(self, other):
# 如果传入的对象是当前类的对象,并且哈希值两者相等,则返回true。
if isinstance(other, people) and hash(other) == hash(self):
return true
return false
# 将字典构造成哈希对象,然后加入集合中
set_1 = set(people(**p) for p in list_a)
set_2 = set(people(**p) for p in list_b)
# 使用集合的差
end_set = set_1 - set_2
for item in end_set:
print(item.first_name, item.last_name, item.phone_num)
# li ruixue 123389278
使用python3.7版本之后的数据类: dataclasses。
from dataclasses import dataclass
@dataclass(unsafe_hash=true)
class people(object):
first_name: str
last_name: str
phone_num: str
set_1 = set(people(**p) for p in list_a)
set_2 = set(people(**p) for p in list_b)
end_set = set_1 - set_2
for item in end_set:
print(item.first_name, item.last_name, item.phone_num)
# li ruixue 123389278
2:__format__对象字符串格式化
1: 需求: 对于自己定义的people对象,打印出不同的信息。
class people(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __format__(self, format_spec):
if format_spec == 'long':
return f'{self.name} is {self.age} years old!'
elif format_spec == 'simple':
return f'{self.name} ({self.age})'
raise valueerror('invalid format spec')
p = people(name='liangshan', age=23)
print('{0:simple}'.format(p))
# liangshan (23)
print('{0:long}'.format(p))
# liangshan is 23 years old!
3:__getitem__方法定义容器
1: 如果我想定义一个容器,那么肯定要考虑的就是容器取长度的方法,容器取第几个元素方法。
class events:
def __init__(self, events):
self.events = events
def __len__(self):
"""自定义长度,将会被用来做布尔判断"""
return len(self.events)
def __getitem__(self, index):
"""自定义切片方法"""
# 直接将 slice 切片对象透传给 events 处理
return self.events[index]
events = events([
'computer started',
'os launched',
'docker started',
'os stopped',
])
# 理解: if events 对调用里面的__len__方法,通过返回是否是0来判断是否是none
# events使用切片的时候,会调用events的__getitem__方法,
# 而我们直接调用self的切片操作,相当于又调用了list中的__getitem__方法。也就是说
# 只要是实例化events的时候,是可以使用切片的,则这个类的对象也是可以使用切片的。
if events:
print(events[1:3])
二:python异常处理三个习惯
1: 只做最精准的异常捕获,每个异常捕获只处理一个容易错的地方。
2:避免抛出高于当前抽象级别的异常外,我们同样应该避免泄露低于当前抽象级别的异常。
3:连续异常,注意使用上下文管理器。
def upload_avatar(request):
"""用户上传新头像"""
try:
avatar_file = request.files['avatar']
except keyerror:
raise error_codes.avatar_file_not_provided
try:
resized_avatar_file = resize_avatar(avatar_file)
except filetoolargeerror as e:
raise error_codes.avatar_file_too_large
except resizeavatarerror as e:
raise error_codes.avatar_file_invalid
try:
request.user.avatar = resized_avatar_file
request.user.save()
except exception:
raise error_codes.internal_server_error
return httpresponse({})
下面代码连续的异常,让我们看到眼花缭乱。
使用上下文管理器管理异常:
class resource(object):
def __enter__(self):
print("连接资源")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("关闭资源")
return true
def operate(self):
1 / 0
with resource() as res:
res.operate()
# 不会报错
- 什么是上下文管理器?
- 答: 一个类实现了
__enter__方法和__exit__方法,则这个类的实例就是上下文管理器。 - 上下文管理器的强大在于
__exit__中你可以决定是抛出异常还是在这里解决。
三:函数返回建议
1: 单个函数,返回值不要有多种类型。
2: partial函数构造函数:
def multiply(x, y):
return x * y
def double(value):
# 返回另一个函数调用结果
return multiply(2, value)
from functools import partial
# partial函数是专门用来基于一个函数来构造一个新的函数的。
# 参数1: 函数名
# 其余参数就是被基于的函数的参数。
def multiply(x, y):
return x * y
double = partial(multiply, 2)
3: 不要返回结果和错误,而是选择抛出异常。
4:谨慎使用none返回:
- none作为默认返回值(没有问题)
- none作为意料之中的返回值(没有问题)
- none作为调用失败返回值, 不推荐使用,更改为异常捕获方式。
5:合理返回空对象:
- 对于返回为none的地方,我们在接受返回值的地方就要使用异常捕获,判断是否为none,这非常的麻烦。
- django 框架里的 anonymoususer 就是一个典型的 null object。
原理:
就是使用一个符合正常结果接口的“空类型”来替代空值返回/抛出异常,以此来降低调用方处理结果的成本。
案例:
class account:
# def __init__ 已省略... ...
@classmethod
def from_string(cls, s):
"""从字符串初始化一个账号
:returns: 如果输入合法,返回 account object,否则返回 nullaccount
"""
try:
username, balance = s.split()
balance = decimal.decimal(float(balance))
except valueerror:
return nullaccount()
if balance < 0:
return nullaccount()
return cls(username=username, balance=balance)
class nullaccount:
username = ''
balance = 0
@classmethod
def from_string(cls, s):
raise notimplementederror
6:返回生成器,而不是返回列表。
# 返回列表
def test01(my_list):
res_list = []
for item in my_list:
res_list.append(item * 2)
return res_list
# 返回生成器
def test02(my_list):
for item in my_list:
yield item * 2
my_list = [1, 2, 3, 4, 5]
print(test01(my_list))
# [2, 4, 6, 8, 10]
print(test02(my_list))
# <generator object test02 at 0x000001a6c693a2a0>
for item in test02(my_list):
print(item)
7:尽量不要使用递归:
- python不支持尾递归优化。
- python递归有最大层数限制。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论