Python基础

⼀、Python基础

[TOC]

Python基础 主要总结Python常⽤内置函数;Python独有的语法特性、关键词 nonlocal, global等;
内置数据结构包括:列表(list), 字典(dict), 集合(set), 元组(tuple) 以及相关的⾼级模块 collections中
的 Counter, namedtuple, defaultdict, heapq模块。⽬前共有 90个⼩例⼦。

1 求绝对值

绝对值或复数的模

abs(-6)

2 元素都为真

接受⼀个迭代器,如果迭代器的 所有元素 都为真,那么返回 True,否则返回 False

all([1,0,3,6])
all([1,2,3])

3 元素⾄少⼀个为真

接受⼀个迭代器,如果迭代器⾥ ⾄少有⼀个 元素为真,那么返回 True,否则返回 False

any([0,0,0,[]])
any([0,0,1])

4 ascii展⽰对象

调⽤对象的repr() ⽅法,获得该⽅法的返回值,如下例⼦返回值为字符串

class Student():
def __init__(self,id,name):
self.id = id
self.name = name
def __repr__(self):
return 'id = '+self.id +', name = '+self.name

xiaoming = Student(id='001',name='xiaoming')
print(xiaoming)
ascii(xiaoming)

5 ⼗转⼆

将 ⼗进制 转换为 ⼆进制

bin(10)

6 ⼗转⼋

将 ⼗进制 转换为 ⼋进制

oct(9)

7 ⼗转⼗六

将 ⼗进制 转换为 ⼗六进制

hex(15)

8 判断是真是假

测试⼀个对象是True, 还是False.

bool([1,0,1])

9 字符串转字节

将⼀个 字符串 转换成 字节 类型

s = "apple"
bytes(s, "utf-8")

10 转为字符串

将 字符类型 、 数值类型 等转换为 字符串 类型

str(100)

11 是否可调⽤

判断对象是否可被调⽤,能被调⽤的对象就是⼀个 callable 对象,⽐如函数 str, int 等都是可被调
⽤的,但是例⼦4 中 xiaoming实例是不可被调⽤的:

callable(str)
callable(int)
xiaoming
callable(xiaoming)

如果想让 xiaoming能被调⽤ xiaoming(), 需要重写 Student类的 __call__⽅法:

class Student():
def __init__(self,id,name):
self.id = id
self.name = name
def __repr__(self):
return 'id = '+self.id +', name = '+self.name
def __call__(self):
print('I can be called')
print(f'my name is {self.name}')

t = Student('001','xiaoming')
t()

12 ⼗转ASCII

查看⼗进制整数对应的 ASCII字符

chr(65)

13 ASCII转⼗

查看某个 ASCII字符 对应的⼗进制数

ord('A')

14 类⽅法

classmethod 装饰器对应的函数不需要实例化,不需要 self参数,但第⼀个参数需要是表⽰⾃⾝类
的 cls 参数,可以来调⽤类的属性,类的⽅法,实例化对象等。

class Student():
def __init__(self,id,name):
self.id = id
self.name = name
def __repr__(self):
return 'id = '+self.id +', name = '+self.name
@classmethod
def f(cls):
print(cls)

15 执⾏字符串表⽰的代码

将字符串编译成python能识别或可执⾏的代码,也可以将⽂字读成字符串再编译。

s = "print('Hello world!')"
r = compile(s,"<string>","exec")
r
exec(r)

16 创建复数

创建⼀个复数

complex(1,2)

17 动态删除属性

删除对象的属性

delattr(xiaoming,'id')
hasattr(xiaoming,'id')

18 转为字典

创建数据字典

dict()
dict(a='a',b='b')
dict(zip(['a','b'],[1,2]))
dict([('a',1),('b',2)])

19 ⼀键查看对象所有⽅法

不带参数时返回 当前范围 内的变量、⽅法和定义的类型列表;带参数时返回 参数 的属性,⽅法列表。

dir(xiaoming)

20 取商和余数

分别取商和余数

divmod(10,3)

21 枚举对象

返回⼀个可以枚举的对象,该对象的next()⽅法将返回⼀个元组。

s = ["a","b","c"]
for i,v in enumerate(s,1):
print(i,v)

22 计算表达式

将字符串str 当成有效的表达式来求值并返回计算结果取出字符串中内容

s = "1+3+4+5"
eval(s)

23 查看变量所占字节数

import sys
s = {"a":"b","b":"a"}
sys.getsizeof(s)

24 过滤器

在函数中设定过滤条件,迭代元素,保留返回值为 True的元素:

fil = filter(lambda x : x > 10,[1,11,2,45,7,6,13])
print(list(fil))

25 转为浮点类型

将⼀个整数或数值型字符串转换为浮点数

float(3)

如果不能转化为浮点数,则会报 ValueError:

float('a')

26 字符串格式化

格式化输出字符串,format(value, format_spec)实质上是调⽤了value的format(format_spec)⽅法。

print("i am {0},age{1}".format("tom",18))

image-20230802164821189

27 冻结集合

创建⼀个不可修改的集合。

frozenset([1,1,3,2,3])

28 动态获取对象属性

获取对象的属性

class Student():
def __init__(self,id,name):
self.id = id
self.name = name
def __repr__(self):
return 'id = '+self.id +', name = '+self.name
xiaoming = Student(id='001',name='xiaoming')
getattr(xiaoming,'id') # 获取xiaoming这个实例的name属性值\

29 对象是否有这个属性

hasattr(xiaoming,'name')
hasattr(xiaoming,'address')

30 返回对象的哈希值

返回对象的哈希值,值得注意的是⾃定义的实例都是可哈希的, list, dict, set等可变对象都是不
可哈希的(unhashable)

hash(xiaoming)
hash([1,2,3])

31 ⼀键帮助

返回对象的帮助⽂档

help(xiaoming)

32 对象门牌号

返回对象的内存地址

id(xiaoming)

33 获取⽤户输⼊

获取⽤户输⼊内容

input()

34 转为整型

int(x, base =10) , x可能为字符串或数值,将x 转换为⼀个普通整数。如果参数是字符串,那么它可能包
含符号和⼩数点。如果超出了普通整数的表⽰范围,⼀个长整数被返回。

int('12',16)

35 isinstance

判断object是否为类classinfo的实例,是返回true

isinstance(xiaoming,Student)

36 ⽗⼦关系鉴定

class undergraduate(Student):
def studyClass(self):
pass
def attendActivity(self):
pass
issubclass(undergraduate,Student)
issubclass(object,Student)
issubclass(Student,object)

如果class是classinfo元组中某个元素的⼦类,也会返回True

issubclass(int,(int,float))

37 创建迭代器类型

使⽤ iter(obj, sentinel), 返回⼀个可迭代对象, sentinel可省略(⼀旦迭代到此元素,⽴即终⽌)

lst = [1,3,5]
for i in iter(lst):
print(i)
class TestIter(object):
def __init__(self):
self.l=[1,3,2,3,4,5]
self.i=iter(self.l)
def __call__(self): #定义了__call__⽅法的类的实例是可调⽤的
item = next(self.i)
print ("__call__ is called,fowhich would return",item)
return item
def __iter__(self): #⽀持迭代协议(即定义有__iter__()函数)
print ("__iter__ is called!!")
return iter(self.l)
T = TestIter()
T()
for i in T:
print(i)

38 所有对象之根

object 是所有类的基类

o = object()
type(o)

39 打开⽂件

返回⽂件对象

f = open('./json.js',mode='r', encoding='GBK')
f.read()

mode取值表:

image-20230802164938454

40 次幂

base为底的exp次幂,如果mod给出,取余

pow(3,2,4)

41 打印

lst = [1,3,5]
print(lst)
print(f"lst: {lst}")
print('lst:{}'.format(lst))
print('lst:',lst)

42 创建属性的两种⽅式

返回 property 属性,典型的⽤法:

class C:
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
# 使⽤property类创建 property 属性
x = property(getx, setx, delx, "I'm the 'x' property.")

使⽤python装饰器,实现与上完全⼀样的效果代码:

class C:
def __init__(self):
self._x = None
@property
def x(self):
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
print(C().x)    

43 创建range序列

  1. range(stop) 2) range(start, stop[,step])

⽣成⼀个不可变序列:

range(100)
range(0,11,1)

44 反向迭代器

rev = reversed([1,4,2,3,1])
for i in rev:
print(i)

45 四舍五⼊

四舍五⼊, ndigits代表⼩数点后保留⼏位:

round(10.0222222, 3)
round(10.05,1)

46 转为集合类型

返回⼀个set对象,集合内不允许有重复元素:

a = [1,2,3,4,5]
set(a)

47 转为切⽚对象

class slice(start, stop[, step])

返回⼀个表⽰由 range(start, stop, step) 所指定索引集的 slice对象,它让代码可读性、可维护性变好。

a = [1,4,2,3,1]
my_slice_meaning = slice(0,5,2)
a[my_slice_meaning]

48 拿来就⽤的排序函数

排序:

a = [{'name':'xiaoming','age':18,'gender':'male'},{'name':'xiaohong','age':20,'gender':'female'}]
sorted(a,key=lambda x:x['age'],reverse=False)
print(a)

49 求和函数

求和:

a = [1,4,2,3,1]
sum(a)
sum(a,10) # 求和的初始值为10

50 转元组

tuple() 将对象转为⼀个不可变的序列类型

i_am_list = [1,3,5]
i_am_tuple = tuple(i_am_list)
i_am_tuple

51 查看对象类型

class type(name, bases, dict)
传⼊⼀个参数时,返回 object 的类型:

class Student():
def __init__(self,id,name):
self.id = id
self.name = name
def __repr__(self):
return 'id = '+self.id +', name = '+self.name
xiaoming = Student(id='001',name='xiaoming')
type(xiaoming)

52 聚合迭代器

创建⼀个聚合了来⾃每个可迭代对象中的元素的迭代器:

x = [3,2,1]
y = [4,5,6]
list(zip(y,x))
a = range(5)
b = list('abcde')
b # ['a', 'b', 'c', 'd', 'e']
[str(y) + str(x) for x,y in zip(a,b)] # ['a0', 'b1', 'c2', 'd3', 'e4']

53 nonlocal⽤于内嵌函数中

关键词 nonlocal常⽤于函数嵌套中,声明变量 i 为⾮局部变量; 如果不声明, i+=1表明 i 为函数
wrapper内的局部变量,因为在 i+=1引⽤(reference)时,i未被声明,所以会报 unreferenced
variable的错误。

import time
def excepter(f):
i = 0
t1 = time.time()
def wrapper():
try:
f()
except Exception as e:
nonlocal i
i += 1
print(f'{e.args[0]}: {i}')
t2 = time.time()
if i == n:
print(f'spending time:{round(t2-t1,2)}')
return wrapper

54 global 声明全局变量

先回答为什么要有 global,⼀个变量被多个函数引⽤,想让全局变量被所有函数共享。有的伙伴可能
会想这还不简单,这样写:

i = 5
def f():
print(i)
def g():
print(i)
pass
f()
g()

f和g两个函数都能共享变量 i ,程序没有报错,所以他们依然不明⽩为什么要⽤ global.

但是,如果我想要有个函数对 i 递增,这样:

def h():
i += 1
h()

此时执⾏程序,bang, 出错了! 抛出异常: UnboundLocalError,原来编译器在解释 i+=1时会把 i
解析为函数 h()内的局部变量,很显然在此函数内,编译器找不到对变量 i 的定义,所以会报错。

global就是为解决此问题⽽被提出,在函数h内,显⽰地告诉编译器 i 为全局变量,然后编译器会在
函数外⾯寻找 i 的定义,执⾏完 i+=1后, i 还为全局变量,值加1:

i = 0
def h():
global i
i += 1
h()
print(i)

56 不⽤else和if实现计算器

from operator import *
def calculator(a, b, k):
return {
'+': add,
'-': sub,
'*': mul,
'/': truediv,
'**': pow
}[k](a, b)
calculator(1, 2, '+') # 3
calculator(3, 4, '**') # 81

57 链式操作

from operator import (add, sub)
def add_or_sub(a, b, oper):
return (add if oper == '+' else sub)(a, b)
add_or_sub(1, 2, '-') # -1
add_or_sub(1, 2, '+') # 3

58 交换两元素

def swap(a, b):
return b, a
print(swap(1, 0)) # (0,1)

59 去最求平均

def score_mean(lst):
lst.sort()
lst2=lst[1:(len(lst)-1)]
return round((sum(lst2)/len(lst2)),1)
lst=[9.1, 9.0,8.1, 9.7, 19,8.2, 8.6,9.8]
score_mean(lst) # 9.1

60 打印99乘法表

打印出如下格式的乘法表

1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81

⼀共有10 ⾏,第 i ⾏的第 j 列等于: j*i,

其中,

i 取值范围: 1<=i<=9

j 取值范围: 1<=j<=i

根据 例⼦分析 的语⾔描述,转化为如下代码:

for i in range(1,10):
for j in range(1,i+1):
print(f"{i}*{j}={i*j}",end="\t")
print()

61 全展开

对于如下数组:

[[[1,2,3],[4,5]]]

如何完全展开成⼀维的。这个⼩例⼦实现的 flatten是递归版,两个参数分别表⽰带展开的数组,输出
数组。

from collections.abc import *
def flatten(lst, out_lst=None):
if out_lst is None:
out_lst = []
for i in lst:
if isinstance(i, Iterable): # 判断i是否可迭代
flatten(i, out_lst) # 尾数递归
else:
out_lst.append(i) # 产⽣结果
return out_lst

调⽤ flatten:

print(flatten([[1,2,3],[4,5]]))
print(flatten([[1,2,3],[4,5]], [6,7]))
print(flatten([[[1,2,3],[4,5,6]]]))

62 列表等分

from math import ceil
def divide(lst, size):
if size <= 0:
return [lst]
return [lst[i * size:(i+1)*size] for i in range(0, ceil(len(lst) / size))]
r = divide([1, 3, 5, 7, 9], 2)
print(r) # [[1, 3], [5, 7], [9]]
r = divide([1, 3, 5, 7, 9], 0)
print(r) # [[1, 3, 5, 7, 9]]
r = divide([1, 3, 5, 7, 9], -3)
print(r) # [[1, 3, 5, 7, 9]]

63 列表压缩

def filter_false(lst):
return list(filter(bool, lst))
lst = [None, 0, False, '', [], 'ok', [1, 2]]
r = filter_false(lst)
print(r) # ['ok', [1, 2]]

64 更长列表

def max_length(*lst):
return max(*lst, key=lambda v: len(v))
r = max_length([1, 2, 3], [4, 5, 6, 7], [8])
print(f'更长的列表是{r}') # [4, 5, 6, 7]
r = max_length([1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11, 11])
print(f'更长的列表是{r}') # [4, 5, 6, 7]

65 求众数

def top1(lst):
return max(lst, default='列表为空', key=lambda v: lst.count(v))
lst = [1, 3, 3, 2, 1, 1, 2]
r = top1(lst)
print(f'{lst}中出现次数最多的元素为:{r}')

66 多表之最

def max_lists(*lst):
return max(max(*lst, key=lambda v: max(v)))
r = max_lists([1, 2, 3], [6, 7, 8], [4, 5])
print(r) # 8

67 列表查重

def has_duplicates(lst):
return len(lst) == len(set(lst))
x = [1, 1, 2, 2, 3, 2, 3, 4, 5, 6]
y = [1, 2, 3, 4, 5]
has_duplicates(x) # False
has_duplicates(y) # True

68 列表反转

def reverse(lst):
return lst[::-1]
r = reverse([1, -2, 3, 4, 1, 2])
print(r) # [2, 1, 4, 3, -2, 1]

69 浮点数等差数列

def rang(start, stop, n):
start,stop,n = float('%.2f' % start), float('%.2f' % stop),int('%.d' % n)
step = (stop-start)/n
lst = [start]
while n > 0:
start,n = start+step,n-1
lst.append(round((start), 2))
return lst
rang(1, 8, 10) # [1.0, 1.7, 2.4, 3.1, 3.8, 4.5, 5.2, 5.9, 6.6, 7.3, 8.0]

71 map实现向量运算

#多序列运算函数—map(function,iterabel,iterable2)
lst1=[1,2,3,4,5,6]
lst2=[3,4,5,6,3,2]
list(map(lambda x,y:x*y+1,lst1,lst2))
### [4, 9, 16, 25, 16, 13]

72 值最⼤的字典

def max_pairs(dic):
if len(dic) == 0:
return dic
max_val = max(map(lambda v: v[1], dic.items()))
return [item for item in dic.items() if item[1] == max_val]
r = max_pairs({'a': -10, 'b': 5, 'c': 3, 'd': 5})
print(r) # [('b', 5), ('d', 5)]

73 合并两个字典

def merge_dict(dic1, dic2):
return {**dic1, **dic2} # python3.5后⽀持的⼀⾏代码实现合并字典
merge_dict({'a': 1, 'b': 2}, {'c': 3}) # {'a': 1, 'b': 2, 'c': 3}

74 topn字典

from heapq import nlargest
# 返回字典d前n个最⼤值对应的键
def topn_dict(d, n):
return nlargest(n, d, key=lambda k: d[k])
topn_dict({'a': 10, 'b': 8, 'c': 9, 'd': 10}, 3) # ['a', 'd', 'c']

75 异位词

from collections import Counter
# 检查两个字符串是否 相同字母异序词,简称:互为变位词
def anagram(str1, str2):
return Counter(str1) == Counter(str2)
anagram('eleven+two', 'twelve+one') # True 这是⼀对神器的变位词
anagram('eleven', 'twelve') # False

76 逻辑上合并字典

(1) 两种合并字典⽅法 这是⼀般的字典合并写法

dic1 = {'x': 1, 'y': 2 }
dic2 = {'y': 3, 'z': 4 }
merged1 = {**dic1, **dic2} # {'x': 1, 'y': 3, 'z': 4}

修改merged[‘x’]=10,dic1中的x值 不变 , merged是重新⽣成的⼀个 新字典 。

但是, ChainMap却不同,它在内部创建了⼀个容纳这些字典的列表。因此使⽤ChainMap合并字典,
修改merged[‘x’]=10后,dic1中的x值 改变 ,如下所⽰:

from collections import ChainMap
merged2 = ChainMap(dic1,dic2)
print(merged2) # ChainMap({'x': 1, 'y': 2}, {'y': 3, 'z': 4})

77 命名元组提⾼可读性

from collections import namedtuple
Point = namedtuple('Point', ['x', 'y', 'z']) # 定义名字为Point的元祖,字段属性有x,y,z
lst = [Point(1.5, 2, 3.0), Point(-0.3, -1.0, 2.1), Point(1.3, 2.8, -2.5)]
print(lst[0].y - lst[1].y)

使⽤命名元组写出来的代码可读性更好,尤其处理上百上千个属性时作⽤更加凸显。

78 样本抽样

使⽤ sample抽样,如下例⼦从100个样本中随机抽样10个。

from random import randint,sample
lst = [randint(0,50) for _ in range(100)]
print(lst[:5])# [38, 19, 11, 3, 6]
lst_sample = sample(lst,10)
print(lst_sample) # [33, 40, 35, 49, 24, 15, 48, 29, 37, 24]

79 重洗数据集

使⽤ shuffle⽤来重洗数据集,值得注意 是对lst就地(in place)洗牌,节省存储空间

from random import shuffle
lst = [randint(0,50) for _ in range(100)]
shuffle(lst)
print(lst[:5]) # [50, 3, 48, 1, 26]

80 10个均匀分布的坐标点

random模块中的 uniform(a,b)⽣成[a,b)内的⼀个随机数,如下⽣成10个均匀分布的⼆维坐标点

from random import uniform
[(uniform(0,10),uniform(0,10)) for _ in range(10)]

81 10个⾼斯分布的坐标点

random模块中的 gauss(u,sigma)⽣成均值为u, 标准差为sigma的满⾜⾼斯分布的值,如下⽣成10个
⼆维坐标点,样本误差(y-2*x-1)满⾜均值为0,标准差为1的⾼斯分布:

from random import gauss
x = range(10)
y = [2*xi+1+gauss(0,1) for xi in x]
points = list(zip(x,y))
points

82 chain⾼效串联多个容器对象

chain函数串联a和b,兼顾内存效率同时写法更加优雅。

from itertools import chain
a = [1,3,5,0]
b = (2,4,6)
for i in chain(a,b):
print(i)

83 操作函数对象

def f():
print("i\'m f")
def g():
print("i\'m g")

[f,g][0]()

创建函数对象的list,根据想要调⽤的index,⽅便统⼀调⽤。

84 ⽣成逆序序列

list(range(10,-1,-1))

第三个参数为负时,表⽰从第⼀个参数开始递减,终⽌到第⼆个参数(不包括此边界)

85 函数的五类参数使⽤例⼦

python五类参数:位置参数,关键字参数,默认参数,可变位置或关键字参数的使⽤。

def f(a,*b,c=10,**d):
print(f'a:{a},b:{b},c:{c},d:{d}')

默认参数c不能位于可变关键字d后:

调用f:

f(1,2,5,width=10,height=20)

可变位置参数 b 实参后被解析为元组 (2,5);⽽c取得默认值10; d被解析为字典.
再次调⽤f:

f(a=1,c=12)

a=1传⼊时a就是关键字参数,b,d都未传值,c被传⼊12,⽽⾮默认值。
注意观察参数 a , 既可以 f(1),也可以 f(a=1) 其可读性⽐第⼀种更好,建议使⽤f(a=1)。如果要强制使
⽤ f(a=1),需要在前⾯添加⼀个星号:

def f(*,a,*b):
print(f'a:{a},b:{b}')

此时f(1)调⽤,将会报错: TypeError: f() takes 0 positional arguments but 1 was given
只能 f(a=1)才能OK.
说明前⾯的 * 发挥作⽤,它变为只能传⼊关键字参数,那么如何查看这个参数的类型呢?借助python
的 inspect模块:

from inspect import signature

for name,val in signature(f).parameters.items():
print(name,val.kind)

可看到参数 a 的类型为 KEYWORD_ONLY,也就是仅仅为关键字参数。

但是,如果f定义为:

def f(a,*b):
print(f'a:{a},b:{b}')

查看参数类型:

for name,val in signature(f).parameters.items():
print(name,val.kind)

可以看到参数 a 既可以是位置参数也可是关键字参数

86 使⽤slice对象

⽣成关于蛋糕的序列cake1:

cake1 = list(range(5,0,-1))
b = cake1[1:10:2]
b # [4, 2]
cake1 # [5, 4, 3, 2, 1]

再⽣成⼀个序列:

from random import randint
cake2 = [randint(1,100) for _ in range(100)]
# 同样以间隔为2切前10个元素,得到切⽚d
d = cake2[1:10:2]
d

你看,我们使⽤同⼀种切法,分别切开两个蛋糕cake1,cake2. 后来发现这种切法 极为经典 ,又拿它去切
更多的容器对象。

那么,为什么不把这种切法封装为⼀个对象呢?于是就有了slice对象。

定义slice对象极为简单,如把上⾯的切法定义成slice对象:

perfect_cake_slice_way = slice(1,10,2)
#去切cake1
cake1_slice = cake1[perfect_cake_slice_way]
cake2_slice = cake2[perfect_cake_slice_way]

cake1_slice # [4, 2]
cake2_slice # [5, 99, 16, 3, 17]

与上⾯的结果⼀致。

对于逆向序列切⽚, slice对象⼀样可⾏:

a = [1,3,5,7,9,0,3,5,7]
a_ = a[5:1:-1]
named_slice = slice(5,1,-1)
a_slice = a[named_slice]
a_ # [0, 9, 7, 5]
a_slice # [0, 9, 7, 5]

频繁使⽤同⼀切⽚的操作可使⽤slice对象抽出来,复⽤的同时还能提⾼代码可读性。

87 lambda 函数的动画演⽰

有些读者反映, lambda函数不太会⽤,问我能不能解释⼀下。

⽐如,下⾯求这个 lambda函数:

def max_len(*lists):
return max(*lists, key=lambda v: len(v))

有两点疑惑:

1.参数 v 的取值?

2.lambda函数有返回值吗?如果有,返回值是多少?

调⽤上⾯函数,求出以下三个最长的列表:

r = max_len([1, 2, 3], [4, 5, 6, 7], [8])
print(f'更长的列表是{r}')

程序完整运⾏过程,动画演示如下:

结论:

1.参数v的可能取值为 *lists,也就是 tuple 的⼀个元素。

2.lambda函数返回值,等于 lambda v 冒号后表达式的返回值。

88 粘性之禅

7 ⾏代码够烧脑,不信试试~~

def product(*args, repeat=1):
pools = [tuple(pool) for pool in args] * repeat
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)

调⽤函数:

rtn = product('xyz', '12', repeat=3)
print(list(rtn))

快去⼿动敲敲,看看输出啥吧~~

89 元类

xiaoming, xiaohong, xiaozhang 都是学⽣,这类群体叫做 Student.

Python 定义类的常见⽅法,使⽤关键字 class

class Student(object):
pass

xiaoming, xiaohong, xiaozhang 是类的实例,则:

xiaoming = Student()
xiaohong = Student()
xiaozhang = Student()

创建后,xiaoming 的 class 属性,返回的便是 Student类

xiaoming.__class__

问题在于, Student 类有 __class__属性,如果有,返回的又是什么?

xiaoming.__class__.__class__

哇,程序没报错,返回 type

那么,我们不妨猜测: Student 类,类型就是 type

换句话说, Student类就是⼀个对象,它的类型就是 type

所以,Python 中⼀切皆对象,类也是对象

Python 中,将描述 Student 类的类被称为:元类。

按照此逻辑延伸,描述元类的类被称为:元元类 ,开玩笑了~ 描述元类的类也被称为元类。

聪明的朋友会问了,既然 Student 类可创建实例,那么 type 类可创建实例吗? 如果能,它创建的实

例就叫:类 了。 你们真聪明!

说对了, type 类⼀定能创建实例,⽐如 Student 类了

Student = type('Student',(),{})
Student

它与使⽤ class 关键字创建的 Student 类⼀模⼀样。

Python 的类,因为又是对象,所以和 xiaoming, xiaohong 对象操作相似。⽀持:

1.赋值

2.拷贝

3.添加属性

4.作为函数参数

StudentMirror = Student # 类直接赋值 # 类直接赋值
Student.class_property = 'class_property' # 添加类属性
hasattr(Student, 'class_property')

元类,确实使⽤不是那么多,也许先了解这些,就能应付⼀些场合。就连 Python 界的领袖 Tim
Peters 都说:

“元类就是深度的魔法,99%的⽤户应该根本不必为此操⼼。”

90 对象序列化

对象序列化,是指将内存中的对象转化为可存储或传输的过程。很多场景,直接⼀个类对象,传输不方便。

但是,当对象序列化后,就会更加⽅便,因为约定俗成的,接⼜间的调⽤或者发起的 web 请求,⼀般
使⽤ json 串传输。

实际使⽤中,⼀般对类对象序列化。先创建⼀个 Student 类型,并创建两个实例。

class Student():
def __init__(self,**args):
self.ids = args['ids']
self.name = args['name']
self.address = args['address']
xiaoming = Student(ids = 1,name = 'xiaoming',address = '北京')
xiaohong = Student(ids = 2,name = 'xiaohong',address = '南京')

导⼊ json 模块,调⽤ dump ⽅法,就会将列表对象 [xiaoming,xiaohong],序列化到⽂件 json.js
中。

import json
with open('json.js', 'w') as f:
json.dump([xiaoming,xiaohong], f, default=lambda obj: obj.__dict__,ensure_ascii=False, indent=2, sort_keys=True)