这一章主要讲python代码的格式问题,可以上网搜一下 python 的 PEP-8规范,了解下 Black , Pylint, Mypy 等几个工具如何使用。
这里的建议是使用 pycharm这个IDE工具,然后安装pylint 工具,可以自动做代码规范检测。
为什么要用python风格来编写代码?
1、每种语言有都有自己的习惯用法,遵循某种语言的习惯用法编写的代码性能更好
2、代码更加紧凑,更容易理解
3、团队开发使用相同的代码模式和结构,团队效率更高
索引和切片
python的索引特点,可以为负数,索引为-1表示最后一个。
my_numbers[2:5] 表示获取元组上第一个数字索引(包含)开始,直到第二个数字索引(不包含)的所有元素
my_numbers[1:7:2] 第三个参数表示步长,表示区间内迭代时每次要跳过多少个元素。
上下文管理器
__enter__ __exit__ 两个魔术方法实现
__enter__ :进入函数前调用
__exit__:退出函数前调用
with open(filename) as fd:
process_file(fd)
import contextlib
def stop_db():
print('stop db')
def start_db():
print('start db')
class DBHandler:
def __enter__(self):
stop_db()
return self # 这里的返回值不是必须的
def __exit__(self, exc_type, exc_val, exc_tb):
start_db() # 不要在__exit__方法返回True,除非一定要返回True
"""
改进版,用contextlib模块实现
"""
@contextlib.contextmanager # 装饰器
def db_handler():
stop_db()
yield # 之前的内容被当做 __enter__方法的一部分执行, 生成器函数被挂起,进入上下文管理器,运行数据备份逻辑,之后的内容被当做__exit__逻辑的一部分
start_db()
def db_backup():
print('backup')
if __name__ == '__main__':
# with DBHandler():
# db_backup()
with db_handler():
db_handler()
运行结果:
(venv) C:\Users\ALIENWARE\PycharmProjects\untitled>python with.py
stop db
backup
start db
使用场景:处理文件、连接等操作,所有有进入和退出逻辑的场景都可以用到上下文管理器。
属性
import re
EMAIL_FORMAT = re.compile(r"[^@]+@[^@]+\.[^@]+")
def is_valid_email(potentially_valid_email: str):
return re.match(EMAIL_FORMAT, potentially_valid_email) is not None
class User:
def __init__(self, username):
self.username = username
self._email = None
@property
def email(self):
return self._email
@email.setter
def email(self, new_email):
if not is_valid_email(new_email):
raise ValueError(f"cant set {new_email} as it is not a valid email")
self._email = new_email
u1 = User('hani')
u1.email = '111@qq.com'
结果:
Traceback (most recent call last):
File "attr.py", line 24, in <module>
u1.email = '111@'
File "attr.py", line 20, in email
raise ValueError("cant set {new_email} as it is not a valid email")
ValueError: cant set {new_email} as it is not a valid email
第一个方法@property 返回私有属性email得值,@property 装饰器是一个查询
第二个方法@email.setter具有前面方法的已定义属性,当<user>.email=<new_email>从调用方代码那里运行时,这个方法将被调用。@<property_name>.setter是执行某些操作的命令
不要为对象的所有属性编写自定义的get_* 和set_* 方法,如果需要修改逻辑,则使用属性。
一个方法应该只做一件事,如果你要运行一个动作并检查状态,那么需要在不同的方法中调用不同的语句
可迭代对象
from datetime import timedelta, date
class DateRangeIterable:
def __init__(self, start_date, end_date):
self.start_date = start_date
self.end_date = end_date
self._present_day = start_date
"""
python先在对象上调用iter()函数,iter()函数调用__iter__这个魔术方法,返回自身表示对象本身是可迭代的
循环的每一步调用该对象上的next()函数,该函数将委托给__next__方法
"""
def __iter__(self):
#return self
current_day = self.start_date
while current_day < self.end_date:
yield current_day # 使用生成器即迭代器对象
current_day += timedelta(days=1)
def __next__(self):
pass
# if self._present_day >= self.end_date:
# raise StopIteration # 必须通过引发StopIteration通知python结束循环
# today = self._present_day
# self._present_day += timedelta(days=1)
# return today
if __name__ == '__main__':
r1 = DateRangeIterable(date(2018, 1, 1), date(2018, 2, 1))
for d in r1:
print(d)
",".join(map(str, r1))
print("{}".format(max(r1)))
创建序列
from datetime import timedelta, date
class DateRangeSequence:
def __init__(self, start_date, end_date):
self.start_date = start_date
self.end_date = end_date
self._range = self._create_range()
def _create_range(self):
days = []
current_day = self.start_date
while current_day < self.end_date:
days.append(current_day)
current_day += timedelta(days=1)
return days
def __getitem__(self, day_no):
return self._range[day_no]
def __len__(self):
return len(self._range)
r1 = DateRangeSequence(date(2018, 1, 1), date(2018, 2, 1))
print(r1[1])
# for day in r1:
# print(day)
容器对象
class Grid:
def __init__(self, width, height):
self.width = width
self.height = height
"""
容器对象是实现了__contains__方法的对象,返回一个boole值,当python的in关键字出现时被调用
"""
def __contains__(self, coord):
x, y = coord
return 0 <= x < self.width and 0 <= y < self.height
if __name__ == '__main__':
coord = [1, 3]
if coord in Grid(2, 9):
print('coord in grid')
else:
print('coord not in grid')
对象的动态属性
class DynamicAttributes:
def __init__(self, attribute):
self.attribute = attribute
def __getattr__(self, item):
if item.startswith("fallback_"):
name = item.replace("fallback_", "")
return f"[fallback resolved] {name}"
raise AttributeError(
f"{self.__class__.__name__} has no attribute {item}"
)
# 请求对象具有的一个属性,并且将它的值作为返回结果
dyn = DynamicAttributes("value")
print(dyn.attribute)
# 对象没有称为fallback_test的东西,所以将使用该值运行__getattr__
print(dyn.fallback_test)
# 创建了一个名叫fallback_new的新属性
dyn.__dict__['fallback_new'] = "new value"
print(dyn.fallback_new)
# 当值不能索引的时候,就会引发AttributeError,getattr返回默认值
print(getattr(dyn, "something", "default"))
可调用对象
from collections import defaultdict
class CallCount:
def __init__(self):
self._counts = defaultdict(int)
# 可调用对象:定义可以充当函数的喜爱那个
def __call__(self, args):
self._counts[args] += 1
return self._counts[args]
cc = CallCount()
print(cc(1))
print(cc(2))
print(cc(2))
print(cc(2))
print(cc(1))
运行结果
(venv) C:\Users\ALIENWARE\PycharmProjects\untitled>python call_object.py
1
1
2
3
2