pythonic风格_如何写出Pythonic风格的代码

如何写出Pythonic风格的代码

索引

>>> my_numbers = (4, 5, 3, 9)

>>> my_numbers[0]

4

>>> my_numbers[1]

5

>>> my_numbers[-1]

9

>>> my_numbers[-2]

3

切片

>>> my_numbers = (4, 5, 3, 9)

>>> my_numbers[0:2]

(4, 5)

>>> my_numbers[0:3:2] # 步长为可选参数

(4, 3)

>>> my_numbers[0:-2] # 只能从左往右

(4, 5)

>>> my_numbers[0:-1:2]

(4, 3)

>>> my_numbers[2:] # 可以缺省起始和终止索引

(3, 9)

>>> my_numbers[:2]

(4, 5)

>>> my_numbers[:] # 相当于浅拷贝

(4, 5, 3, 9)

>>> my_numbers[::]

(4, 5, 3, 9)

>>> interval = slice(0, 3, 2)

>>> my_numbers[interval] # => my_numbers[0:3:2]

(4, 3)

>>> interval = slice(None, 3)

>>> my_numbers[interval] == my_numbers[:3]

True

创建自定义序列类型

class Items:

def __init__(self, *values):

self._values = list(values)

def __len__(self):

return len(self._values)

def __getitem__(self, item):

return self._values.__getitem__(item)

上面的例子封装了内置的list结构。另外一种是使用继承,通过拓展基类collections.UserList。

当你需要完完全全实现自己的序列结构时,请牢记两点:

按照范围切片时,结果应该是相同类型的实例序列。

在切片提供的范围内,请尊重Python使用的语义,排除最后的元素。

另外range也可以进行切片

>>> range(1, 100)[25:50]

range(26, 51)

上下文管理

上下文管理器是Python提供的独特功能,它十分有用,非常适合这样的模式:运行一些代码,需要前置和后置条件。例如处理文件:open->处理->close。

fd = open(filename)

try:

process_file(fd)

finally:

fd.close()

使用with

with open(filename) as fd:

process_file(fd)

上下文管理器依赖两个魔法方法__enter__和__exit__,分别对应前置和后置处理逻辑。

通过类实现上下文管理器

def stop_database():

print("systemctl stop postgresql.service")

def start_database():

print("systemctl start postgresql.service")

def db_backup():

print("pg_dump database")

class DBHandler:

def __enter__(self):

stop_database()

return self # "with xxx as obj" obj is self here.

def __exit__(self, exc_type, ex_value, ex_traceback):

start_database()

def main():

with DBHandler():

db_backup()

通过方法实现上下文管理器,需要使用contextlib.contextmanager装饰器

import contextlib

@contextlib.contextmanager

def db_handler():

stop_database()

yield # "with xxx as obj" obj is "yield {obj}".

start_database()

with db_handler():

db_backup()

甚至连with也不用,需要使用contextlib.ContextDecorator基类

import contextlib

class dbhandler_decorator(contextlib.ContextDecorator):

def __enter__(self):

stop_database()

def __exit__(self, ext_type, ex_value, ex_traceback):

start_database()

@dbhandler_decorator()

def offline_backup():

print("pg_dump database")

但是这种用法你没法使用__enter__函数的返回值

使用上下文管理器处理异常

import contextlib

with contextlib.suppress(DataConversionException):

parse_data(input_json_or_dict)

如果引发DataConversionException,直接跳过。

属性

单下划线表示只会从内部访问(保护)的属性和方法。对象只应该公开那些外部调用者关心的属性和方法,也就是接口,一切不是严格属于接口的都应该使用单下划线。

双下划线并非定义私有变量的方式,访问双下划线会导致AttributeError,这是因为双下划线会导致重命名:

class Connector:

def __init__(self):

self.__timeout = 60 # ___

def __run(self):

print('Run')

conn = Connector()

print(conn.__timeout) # raise AttributeError

print(conn._Connector__timeout) # 60

print(conn.__run) # raise AttributeError

print(conn._Connector__run) # function

双下划线是非Pythonic方法。如果您需要将属性定义为私有的,使用单个下划线,并遵守Pythonic约定,即它是私有的属性。

使用@properties和@xxxx.setter装饰器

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):

self._email = new_email

迭代对象

python确定对象是否可迭代的,主要依据两点:

是否实现了__next__或__iter__迭代方法

是否是一个序列,实现了__len__和__getitem__

python会依照顺序尝试调用这些方法来迭代对象

class A:

def __init__(self, count=0):

self._count = count

def __iter__(self):

return self

def __next__(self):

if self._count >= 5:

raise StopIteration

self._count += 1

return self._count

class B:

def __iter__(self):

return A(1)

a = list(A())

b = list(B())

print(a) # [1, 2, 3, 4, 5]

print(b) # [2, 3, 4, 5]

调用__iter__返回的值再调用__next__

或者__iter__直接返回生成器

from datetime import date, timedelta

class DateRangeContainerIterable:

def __init__(self, start_date, end_date):

self.start_date = start_date

self.end_date = end_date

def __iter__(self):

current_day = self.start_date

while current_day < self.end_date:

yield current_day

current_day += timedelta(days=1)

d = DateRangeContainerIterable(date(2018, 1, 1), date(2018, 1, 5))

print(", ".join(map(str, d))) # '2018-01-01, 2018-01-02, 2018-01-03, 2018-01-04'

包含对象

flag = element in container

# => flag = container.__contains__(element)

动态获取属性

python调用存在的属性会调用__getattribute__,当属性不存在时,会调用__getattr__。

可调用对象

当我们调用一个对象object(*args, *kwargs)时,实际上转换为了object.__call__(args, **kwargs)

from collections import defaultdict

class CallCount:

def __init__(self):

self._counts = defaultdict(int)

def __call__(self, argument):

self._counts[argument] += 1

return self._counts[argument]

cc = CallCount()

print(cc(1)) # 1

print(cc(2)) # 1

print(cc(1)) # 2

print(cc(1)) # 3

print(cc(3)) # 1

魔法方法总结

[图片上传失败...(image-bd2aa1-1571013774916)]

Python注意事项

不要将引用类型的对象作为函数的默认参数,否则结果不可预期

def wrong_user_display(user_metadata: dict = {"name": "John", "age": 30}):

name = user_metadata.pop("name")

age = user_metadata.pop("age")

return f"{name} ({age})"

print(wrong_user_display()) # 'John (30)

print(wrong_user_display({"name": "Jane", "age": 25})) # John (25)

print(wrong_user_display()) # raise KeyError: 'name'

拓展内置数据类型

拓展内置数据类型的正确方式是使用collections模块

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值