python代码规范方面的书_python代码规范

空格

括号内侧,左括号,右括号前面,不加空格,多重括号不加空格

a = ((b + c)*d - 5)

导入

# 不写成一行导入,分多行

import sys

import os

# 不能使用 from xxx import *

from xxx import yyy

注释

函数

def load_batch(path):

"""

功能描述

参数

返回值

异常描述

"""

all

判断可迭代参数是否全是true,是的话返回true,否则返回false

all(['a', 'b', 'c', 'd']) true

Counter

计数器,追踪元素的出现次数

obj = collections.Counter("aabbcc") # Counter({'a': 2, 'b': 2, c: '2'})

obj.elements() ['a', 'a', 'b', 'b', ...] # 遍历打印所有元素

obj.most_common(2) # 打印前n个次数最多的

obj.update(['22', '55']) # 增加元素 subtrace 减少元素

items 枚举

class TreeError():

"""

功能描述

接口

"""

命名

Classes CapWords

Exceptions CapWords

Global/Class Constants CAPS_WITH_UNDER 常量用大写

Modules lower_with_under

Packages lower_with_under

instance Variables lower_with_under

method names lower_with_under

method parameters lower_with_under

local variables lower_with_under

# 私有方法

self._member = 1 # 类的私有变量

self._privite_func(self) # 类的私有函数

self.__update() # 会被解释器修改为_ClassName__update,防止与子类的同名函数冲突

编码

判断

is 判断是否指向一个对象

== 调用eq方法判断是否等价(判断两对象的值是否相等)

# 与None比较用is

bad_inst is None

_ all_

__all__可以放入 from module import *导出的方法

__all__ = ["external_function"]

def external_function():

pass

def internal_function():

pass

字符串

浮点

# 精确计算使用decimal模块

from decimal import Decimal, getcontext

print('%.20f' % 3.14)

print(Decimal('3.14')) # 精确始终以两位小数表示

getcontext().prec = 6

Demical(1) / Demical(7) # Decimal('0.142857')

拼接

使用% format join代替+ +=完成字符串格式化

x = '{}, {}'.format(impreactive, expletive)

函数

@static和@class

class A(object):

def m1(self, n):

print("self:", self)

@classmethod

def m2(cls, n):

print("cls", cls)

@staticmethod

def m3(n):

pass

a=A()

a.m1(1) # self: <__main__.a object at>

a.m2(1) # cls:

a.m3(1)

代码首先运行class创建类A对象,初始化类属性方法,接着执行a=A(),调用构造器,构造实例对象a,调用a.m1(1),self和a都指向实例对象,A.m2(1),python内部隐式的姜磊对象传给cls,cls和A都指向类对象

# 静态方法使用场景:不需要访问任何实例方法或属性

# 类方法:使用工厂方法创建实例对象

class date:

def __new__(cls, year, month=None. day=None):

self = object.__new__(cls)

self._year = year

self._month = month

self._day = day

return self

@classmethod

def fromtimestamp(cls, t):

y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)

return cls(y, m, d)

可变参数不使用默认值

参数的默认值在方法定义执行时就设定了,默认值只会被设定一次,当函数定义后,每次调用会进行预计算,如果参数是列表或字典,方法修改这个值会影响到下一次调用

def foo(bar=[]): #

bar.append("baz")

return bar

foo() # ["baz"]

foo() # ["baz", "baz"]

# 修改

def foo(bar=None):

if bar is None: not

bar = []

列表

预留空间

list在append时会分配一块更大的内存,将原来成员拷贝到新内存,再将新append的成员也拷贝到新内存空间,释放来内存

members = []

for i in range(1, 10000000):

members.append(i)

改为:

members = [None] * 1000000

切片

a = [1,2,3,4,5,6,7,8]

a[2::2] # 3 5 7

a[-2::-2] # 7 5 3 1

a[-2:2:-2] # 7 5

a[2:2:-2] # []

元祖

内容不变用tuple代替list

list是动态的数组,而tuple是静态数组,list需要更多空间跟踪成员状态,另外,小于等于20的元祖,python会对其缓存,当元祖不再使用,并不会立即释放内存,而是保留以备后用

字典

使用dict.get()获取

检查

类型检查

# 用isinstace去检查参数类型,不能用is, len(), 均不安全

def sample_sort_list(sample_inst):

if sample_inst is []: # 会被()绕过

...

if not isinstace(sample_inst, list):

raise TypeError()

循环

推导式

# 推导式(comprehension)精炼写法

odd_num_list = [i for i in range(100) if i % 2 == 1]

# for太多场景用itertools

cubes = [(x, y, z) for x in lengths for y in widths for x in heights]

cubes = list(itertools.product(lengths, widths, heights))

enumerate

# 在处理单个元素时,可以使用enumerate给元素加上序号形成元祖

my_list = ['a', 'b', 'c']

for x in enumerate(my_list):

print(x)

迭代器

list comprehension可以用来替代lamda表达式的map,reduce,从已有list生成新数据,而generator comperhension无需定义一个包含yield语句的函数,就可以生成新的generator,二者在内存上相差悬殊,时间上差不多

错误:even_cnt = len([x for x in range(10) if x % 2 == 0])

正确:enen_cnt = sum(1 for x in range(10) if x % 2 == 0)

异常

finally

# 使用finally保证对象释放

handle = open(r"/tmp/sample_data.txt")

try:

data = handle.read()

except UnicodeDecodeError as decode_error:

print(decode_error)

finally:

handle.close()

# finally中避免使用return或break,会将前面暂存的异常丢弃掉

try:

1/0

finally:

return 42

返回42 不会报错

try:

return 'try'

finally:

return 'finally'

返回finally

并行

GIL

python通过CPython, PyPy, Psyco等不同的解析器来解析执行,GIL是global interpreter lock,为了防止多线程并发执行机器码的互斥锁(mutex)

IO IO io IO

thread1 运行 | 运行 |

thread1 | 运行 |

thread3 | 运行 |

release GIL|acquire GIL

单核CPU调度多个任务,共享一个全局锁,在CPU执行的线程得到锁,直到IO操作或timer tick让出CPU,其余时间只能等待

但在多核中,线程2需要在CPU2中执行,必须等待CPU1上执行的线程1释放GIL(全局),如果线程1是由于IO阻塞让出GIL,线程2必定拿到锁,但是如果1是因为timer ticks计数满100让出GIL,线程1会和2公平竞争,python2.x中线程1不会动态调整优先级,因此大概率2抢不过1,2只能眼睁睁看着1在CPU执行。。

扩展到8核,一核有难,八核围观,多核多线程比单核多线程更差,因为单核下多线程每次释放GIL,唤醒的那个线程能立即获取到GIL无缝执行,而多核下CPU0释放GIL后,其他核的线程会竞争锁,但GIL可能又会被CPU0拿到,导致其他核被唤醒的线程会醒着等待到切换时间后进入待调度状态,造成线程颠簸(thrashing),效率更低

因此,多核下python要想充分利用多进程,就用多进程,每个进程都有各自的GIL,效率自然更高

# 例

def counter():

i = 0

for _ in range(100000):

i = i + 1

return True

def main1():

for tid in range(2):

t = threading.Thread(target=counter)

t.start()

t.join()

def main2():

thread_dict = {}

for tid in range(2):

t = threading.Thread(target=counter)

t.start()

thread_dict[tid] = t

for i in range(2):

thread_dict[i].join()

main1()

main2()

python2中7s,24s

python3中14.39s,14.10s

因为main1在第一次循环就阻塞了,第二个线程还没起,相当于串行,main2两个线程并行,因为GIL的存在,1和2不断的抢占切换消耗时间

线程安全

线程释放GIL有两种情况:

1. 线程A进入IO操作主动释放GIL,由于A的IO操作等待时间不定,等待的B一定能得到GIL锁

2. 线程A因为解释器执行了1000字节码的指令或不间断运行了15ms放弃了GIL,A和B会同时竞争GIL锁,python3会动态调整线程的优先级,线程B的优先级较高,也不一定B就抢到锁

n = 0

def add():

global n

n = n + 1

def sub():

global n

n = n - 1

加法,减法非原子操作,分为

1.LOAD_GLOBAL(加载全局变量) 2.LOAD_CONST(加载常量) 3.二进制加/减法 4.STORE_GLOBAL(存储全局变量),当加减法并行,加法首先拿到锁,执行到第三步,减法抢到GIL,此时n还是0,也执行到第三步,加法抢去,将运算结果给全局n=1,反之开始是减法,被加法抢去后,最后变成-1,这样导致线程1和2分别对n进行了10000次运算后不是0

总结:多线程不是IO密集型,并且计算不是原子级操作时,才需要考虑线程安全问题

使用场景

1. 在IO密集型(文件操作)应用中,由于存在GIL,线程中的IO操作会使得线程立即释放GIL,切换到其他非IO线程继续操作,提高执行效率

def slow_systemcall(n):

for x in range(100):

open("test_%s" % n, "a").write(os.urandom(10) * 100000)

for _ in range(N):

slow_systemcall(_) 5.179

for _ in range(N):

thread = Thread(target=slow_systemcall, args=("t_%s"%_,)) 1.451

2. 计算密集型(循环,计数)应用,ticks会很快达到计数阈值,触发GIL释放再竞争,多个线程来回切换消耗资源,效率低下

def factorize(number):

for i in range(1, number + 1):

if number % i == 0:

yield i

class FactorizeThread(Thread): def __init__(self, number):

Thread.__init__(self)

self.number = number

def run(self):

self.factors = list(factorize(self.number))

for number in numbers:

list(factorize(number)) 0.319

for number in numbers:

thread = FactorizeThread(number)

thread.start()

threads.append(thread) 0.539

for t in threads:

t.join()

文件操作

with

推荐使用with,自动关闭文件

whith open(r'') as somefile:

for line in somefile:

print(line)

拷贝

copy,deepcopy

copy浅拷贝:拷贝一个对象,但对象的属性还是引用原来的,对于可变类型,列表字典,只复制引用,基于引用所做的改变还是会影响到被引用对象

deepcopy:深拷贝,创建一个新容器对象,包含原有对象元素全新拷贝的引用

对于字符串和其他原子对象,不存在拷贝说法,只是新创建对象,替换老的

a = [1, 2, ['x', 'y']]

b = a

c = copy.copy(a)

d = copy.deepcopy(a)

a.append(3)

a[2].append('z')

a.append(['x', 'y'])

a # [1, 2, [x, y, z], [x, y]]

b # [1, 2, [x, y, z], [x, y]]

c # [1, 2, [x, y, z]]

d # [1, 2, ['x', 'y']]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值