《编写整洁的python代码》学习笔记--第1章简介、代码格式和工具+第2章python风格代码

这一章主要讲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

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值