Effective Python(原书第二版) -2023年10月25日

笔记目录


前言

Python ; Effective Python ; 编程语言 ; 笔记


零、目录

1.列表与字典 : 11 - 18条

2.函数 : 19 - 26条

3.推导与生成 : 27 - 36条

4.类与接口 : 37 - 43条

5.元类与属性 : 44 - 51条

6.并发与并行 : 52-64条

7.稳定与性能 : 65 -74条

8.调试与测试 : 75 - 81条

9.协作开发 : 82 - 90条

一、11条.对序列切片

重复的任务 -> 自动处理程序

列表(list)类型整理重复任务 : 每项任务当成列表中的一个元素

列表的互补结构 : 字典(dict) : 键值对 (哈希表/关键矩阵) 
//访问与赋值耗费时间为常量, 适合保存动态信息

1.Somelist[start : end]

从头取省略起始下标0, 选到序列末尾, 则省略终止下标
切片允许 起始/终止 下标越界 // 取开头n个元素 : a[:n], 取末尾n个元素 : a[-n:]

// a is b; a,b指向同一个对象 ; 开发时有风险, 建议再创一个对象防止维护麻烦
a = b



二、12条.不要在切片里同时指定起止下标与步长

1.somelist[start : end : stride]

字符串反转 : a[::-1]
#如果字符串的内容都是 ASCII 字符,那么直接在字符串前面添加 b 前缀就可以转换成 bytes
1.bytes类型
2.Unicode
// UTF-8 不能

三、13条.通过带*的 unpacking 表达式捕获多个元素, 不用切片

优点 : 动态更新?
// 带星号(*)的表达式
*args // 元组
**k // 字典, 会吃掉提示 -> any



四、14条.sort方法中的key参数表示复杂排序

默认从小到大

1.lambda表达式(匿名函数)

语法格式 : name = lambda [list] : 表达式
# 定义 lambda 表达式,必须使用 lambda 关键字;
# [list] 作为可选参数,等同于定义函数是指定的参数列表;
# value 为该表达式的名称。

等价于 : 

def name(list):
	return 表达式
name(list)
# Eg
def add(x, y):
    return x+ y
print(add(3,4))

# add = lambda x,y:x+y
# print(add(3,4))
优势 :
1. 对于单行函数,使用 lambda 表达式可以省去定义函数的过程,让代码更加简洁;
2. 对于不需要多次复用的函数,使用 lambda 表达式可以在用完之后立即释放,提高程序执行的性能。

2._ repr _

这个函数是一个特殊方法(special method)_ _repr_ _,它在Python中用于定义对象的字符串表示形式。
当你使用print()函数或者在交互式环境中直接输入对象名时,Python会调用__repr__方法来获取对象的可打印表示形式。

这个_ _repr_ _方法返回一个字符串,用于表示Tool类的对象。在返回的字符串中,使用了f-string格式化字符串的语法,它允许在字符串中嵌入表达式。
!r表示将self.name的值使用repr()函数进行转换,确保以可打印的形式显示字符串,并将其插入到格式化字符串中。

所以,__repr__方法的返回值将类似于Tool('工具名称', 权重)的形式,其中'工具名称'是self.name的值,权重是self.weight的值。

通过定义__repr__方法,你可以为你的自定义类提供一个可读性良好的字符串表示形式,便于调试和输出对象信息。

3._ init _

这个函数是一个特殊方法(special method)_ _init_ _,在Python中用于初始化对象的方法。
当你创建一个类的新实例时,_ _init_ _方法会被自动调用。

self参数表示类的实例对象本身。

在__init__方法的内部,使用self.name = name和self.weight = weight的语句
将传入的name和weight参数的值赋给了对象的属性name和weight。
这样,在创建类的实例时,你可以提供具体的名称和重量值来初始化对象的这两个属性。

4.sort方法排序 类的对象 构成的列表

class Tool :
	def __init__(self, name, weight):
		self.name = name
		self.weight = weight

	def __repr__(self) :
		return f'Tool({self.name!r}, {self.weight})'

# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
    tools = [
        Tool('C', 6),
        Tool('A',3.5),
        Tool('B', 88),
    ]

    print('Unsorted : ', repr(tools))

    tools.sort(key = lambda x : x.name) # 输入的值为 x 返回值为 x.name

    print('\nsorted1 : ', repr(tools))

    tools.sort(key=lambda x: x.weight) # 输入的值为 x 返回值为 x.weight

    print('\nsorted2 : ', repr(tools))


'''''''''''
执行结果 :
Unsorted :  [Tool('C', 6), Tool('A', 3.5), Tool('B', 88)]

sorted1 :  [Tool('A', 3.5), Tool('B', 88), Tool('C', 6)]

sorted2 :  [Tool('A', 3.5), Tool('C', 6), Tool('B', 88)]
'''''''''''


5.sort方法排序 字符串 构成的列表

# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
    words = ['A', 'b', 'C', 'd']
    
    words.sort()
    print('case sensitive :', words)
    
    words.sort(key = lambda x : x.lower())
    print('case insensitive :', words)
    
'''''''''
case sensitive : ['A', 'C', 'b', 'd']   # 大写 比 小写的 ASCII码小
case insensitive : ['A', 'b', 'C', 'd']
'''''''''

6.sort方法排序 元组 构成的列表

需要多标准排序  # (A, B), 当A项相同时 比较B项 :
1. 尝试转换成 : 元组(tuple) # 优先
	tuple 本身就是可比较的
2. 拆分成多轮排序, 权重的放在后轮

class Tool :
	def __init__(self, name, weight):
		self.name = name
		self.weight = weight

	def __repr__(self) :
		return f'Tool({self.name!r}, {self.weight})'

# 按间距中的绿色按钮以运行脚本。
if __name__ == '__main__':
    tools = [
        Tool('C', 6),
        Tool('A',6),
        Tool('B', 88),
    ]
    tools.sort(key = lambda x: (x.weight, x.name)) # 先对比权重, 再对比名字
    print(tools)

'''
执行结果 :
[Tool('A', 6), Tool('C', 6), Tool('B', 88)]
'''

7.sort方法排序_降序

  1. sort(key = , reverse = True)
 tools.sort(key = lambda x: (x.weight, x.name), reverse = True)
  1. 一元减操作符
# 存在一项指标为数字时

tools.sort(key = lambda x: ( - x.weight, x.name))


五、15条.不依赖给字典添加条目时所用的顺序

3.5 python 用 哈希表 实现, 不保证迭代顺序 与 插入顺序一致
3.7 python 可以确信迭代顺序 与 插入顺序一致

python中很容易定义与 标准字典 很像但本身不是 dict 的实例的对象 # 不保证迭代顺序 与 插入顺序一致

  1. 不依赖插入时的顺序编写代码
  2. 程序运行时明确判断是不是标准字典
  3. 给代码添加类型注解并做静态分析


六、16条.用get处理键不在字典中的情况, 不要使用in 与 KeyError

1.使用 in 处理键不在字典中的情况

counters = {
	'A' : 2,
	'B' : 1,
}

key = 'C'

if key in counters :
	count = counters[key]
else :
	count = 0

counters[key] = count + 1

2.使用 KeyError 处理键不在字典中的情况

counters = {
	'A' : 2,
	'B' : 1,
}

try :
	count = counters[key]
except KeyError:
	count = 0

counters[key] = count + 1

3.使用 get 处理键不在字典中的情况

# 最简短 : get(key, 默认值)
count = counters.get(key, 0)
counters[key] = count + 1

4.字典中存储列表

votes = {
	'A' : ['Bob', 'Alice'],
	'B' : ['Coco', 'Deb'],
}
key = 'C'
who = 'Jam'

names = votes.get(key)
if names is None:
	votes[key] = names = []

names.append(who)

'''
更好的写法_省一行 :
if (name := votes.get(key)) is None:
	votes[key] = names = []

names.append(who)
'''

5.setdefault



七、17条.用defaultdict处理内部状态缺失的元素, 不用setdefault

defaultdict 可以更方便地处理字典中不存在的键,而不需要显式使用 setdefault 方法。
defaultdict在某些情况下可以简化代码逻辑,并提供更好的可读性。

1.setdefault

setdefault 是字典的方法,用于获取指定键的值,
如果键不存在,则用该键与用户提供的默认值关联起来并插入字典中, 并返回该值
如果键存在,则返回对应的值。

2.defaultdict

defaultdict 是 collections 模块中的一个类,它是一个带有默认值的字典。
在创建 defaultdict 时,需要指定一个默认值的类型或一个可调用对象来生成默认值。
当访问字典中不存在的键时,defaultdict 会返回默认值而不是抛出 KeyError 异常。


八、18条._ missing _ 构造依赖键的默认值

九、19条.不要把函数返回的多个数值拆分到三个以上变量中

十、20条.遇到意外状况时应该抛出异常, 不要返回None

十一、21条.在闭包里面使用外围作用域中的变量

十二、22条.用数量可变的位置参数给函数设计清晰的参数列表

十三、23条.用关键字参数来表示可选的行为

十四、24条.用None和docstring来描述默认值会变的参数

十五、25条.用只能以关键字指定和只能按位置传入的参数来设计清晰的参数列表

十六、26条.用functools.wraps定义函数修饰器



四、类与接口

4.1 37条 : 用组合起来的类实现多层结构, 不要用嵌套的内置类型

4.1.1 用字典来维护内部状态的那些代码越写越复杂

4.1.1.1
需求 : 定义一个类, 把学生的名字当做键保存到 _grades 字典中
class SimpleGradebook:

    def __init__(self):
        self._grades = {}

    def add_student(self, name):
        self._grades[name] = []

    def report_grade(self, name, score):
        self._grades[name].append(score)

    def average_grade(self, name):
        grades = self._grades[name]
        return sum(grades) / len(grades)

if __name__ == "__main__" :
    book = SimpleGradebook()

    book.add_student('Isaac Newton')
    book.report_grade('Isaac Newton', 90)
    book.report_grade('Isaac Newton', 95)
    book.report_grade('Isaac Newton', 85)

    print(book.average_grade('Isaac Newton')) # 90.0
4.1.1.2

知识点补充 : defaultdict(list)

defaultdict(list) 和普通的字典创建方式相比,主要的区别在于它默认将不存在的键映射到一个空列表 ([]),而不是抛出一个 KeyError。

这意味着你可以在默认情况下将一个键指向一个列表,而不需要显式地检查键是否存在或者初始化一个空列表。

例如,假设你有一个字典 grades:

grades = defaultdict(list)

然后你可以这样做:

grades['John'].append(85)
grades['Jane'].append(90)

如果你使用一个普通的字典,这样的操作将会引发一个 KeyError,
因为字典中还没有 'John' 或 'Jane' 这样的键。

但是使用 defaultdict(list),它会自动创建一个空列表,
然后将成绩添加到这个列表中,而不会引发异常。
需求 : 拓展类的功能, 按照科目保存成绩(而不是把所有科目的成绩存在一起)
from collections import defaultdict

class BySubjectGradebook:

    def __init__(self):
        self._grades = {} # Outer dict

    def add_student(self, name):
        self._grades[name] = defaultdict(list) # {name : {wait : [] ...}}

    def report_grade(self, name, subject, grade):
        by_subject = self._grades[name] # 特殊列表字典
        grade_list = by_subject[subject] # 直接创建一个 subject : []
        grade_list.append(grade)

    def average_grade(self, name):
        by_subject = self._grades[name]
        total, count = 0, 0
        for grades in by_subject.values():
            total += sum(grades)
            count += len(grades)
        return total / count

if __name__ == "__main__" :

    book = BySubjectGradebook()

    book.add_student('Albert Einstein')
    book.report_grade('Albert Einstein', 'Math', 75)
    book.report_grade('Albert Einstein', 'Math', 65)
    book.report_grade('Albert Einstein', 'Gym', 90)
    book.report_grade('Albert Einstein', 'Gym', 95)

    print(book.average_grade('Albert Einstein')) # 81.25
4.1.1.3
需求 : 拓展类的功能,记录每次考试在列表中的权重
from collections import defaultdict

class WeightedGradebook:
    def __init__(self):
        self._grades = {}

    def add_student(self, name):
        self._grades[name] = defaultdict(list)

    def report_grade(self, name, subject, score, weight):
        by_subject = self._grades[name]
        grade_list = by_subject[subject]
        grade_list.append((score, weight)) # 用元组同时存储(成绩, 权重)


    def average_grade(self, name):
        by_subject = self._grades[name]
        score_sum, score_count = 0, 0
        for subject, scores in by_subject.items():
            subject_avg, total_weight = 0, 0

            for score, weight in scores: # 元组的遍历操作
                subject_avg += score * weight
                total_weight += weight
            score_sum += subject_avg / total_weight 
            score_count += 1
    
        return score_sum / score_count

if __name__ == "__main__" :

    book = WeightedGradebook()

    book.add_student('Albert Einstein')
    book.report_grade('Albert Einstein', 'Math', 75, 0.05)
    book.report_grade('Albert Einstein', 'Math', 65, 0.15)
    book.report_grade('Albert Einstein', 'Math', 70, 0.80)
    book.report_grade('Albert Einstein', 'Gym', 100, 0.40)
    book.report_grade('Albert Einstein', 'Gym', 85, 0.60)

    print(book.average_grade('Albert Einstein')) # 80.25

4.1.2 优化·把多层嵌套的内置类型重构为类体系


方案 :
1. 先从依赖树的底层做起(考虑怎么记录某科目的单次考试成绩与权重)
		(1) 元组 ; 局限:元组的元素只能通过位置区分(元素不宜超过两个)
		(2)具名元组(定义出小型类以表示不可变的数据); 
			局限:无法指定默认的参数值, 需要完全控制实例化的用法 # 已经出现具名字典了
2. 表示学生的Student类
3. 表示成绩册的Gradebook容器类


补充

1.列表

1. 给列表指定一个复数的名称
2. 输出时, 可使用 title() 方法, 首字母大写
3. 索引从 0 开始, -1返回最后一个列表元素
4. 索引返回的值(字符串), 不包含 
5. 方括号和引号
6. 可以像其他变量一样使用列表中的各个值

2.字典

表示形式 : dict = {' ' : ' ', ' ' : ' ', }
键和值之间用冒号分隔, 键-值对直接用逗号分隔, 可将任何对象用作字典中的值

1.创建一个空字典

dict = { }

2.添加键-值对

dict['color'] = 'red'

3.删除键-值对

del 字典名[键名]
del dict['color']

4.修改字典中的值

字典名[键名] = '新值'
dict = { }
dict['color'] = 'red'   # 字典中没有则新建
dict['color'] = 'white' # 字典中有则修改

5.访问字典中的值

字典名[键名]

6.遍历所有的键-值对

字典名.items()
返回一个键-值对列表
# for k, v in dict.items():
for key,value in dict.items():
	print("\nKey: " + key)
	print("Value: " + value)

7.遍历字典中的所有键

字典名.key()
for key in dict.key():
# for key in dict:

8.按顺序遍历字典中的所有键

sorted() # 获得特定顺序排列的列表 的 副本
for key in sorted(dict.key()):

9.遍历字典中的所有值

字典名.values()
set() # 集合, 剔除重复项
for value in set(dict.value()):
# for value in dict.value():

3.嵌套

1.列表中存储字典

2.字典中存储列表

3.字典中存储字典


4.函数



总结

23/06/21 - 未完结

  1. https://zhuanlan.zhihu.com/p/367034667

cmd : where python

2.Linux 中 SSH 连接配置文件位于 /etc/ssh/sshd_config

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值