本文内容摘自朱雷老师著《Python工匠》一书内容,为学习笔记摘录。
一、数值基础
在Python中,一共存在3种内置数值类型:整型(int)、浮点型(float)、复数类型(complex)。创建如下:
# 定义一个整型
score = 100
# 定义一个浮点型
temp = 37.2
# 定义一个复数
com = 3+4j
# 创建一个复数
z = complex(3, 4) # 创建一个复数,实部为3,虚部为4
print(z) # 输出:3+4j
在大多数情况下,主要使用前两种类型:int和float。二者之间可以通过个值得内置方法进行转换:
>>> i = 1_00_000_0000
>>> i
1000000000
>>> j = 2_345_5678
>>> j
23455678
>>>?
在定义数字字面量时,如果数字特别长,可以通过插入下划线 _ 分隔符来让它变得更易读,如:
# 以“千”为单位分割数字
>>> i = 1_000_000_000
>>> i
1000000000
# 其实,可以随意位数间隔,比如
>>> j = 2_345_5678
>>> j
23455678
>>>?
一个问题:浮点数精度
如果在Python命令行输入0.1 + 0.2 ,你会看到这样的奇景:
>>> 0.1+0.2
0.30000000000000004
>>>?
解决这样的问题,可以使用Python内置模块decimal。如果程序需要精确的浮点数计算,可以考虑使用decimal.Decimal对象来替代普通浮点数,它在做四则运算时候不会损失任何精度:
>>> from decimal import Decimal
# 注意:这里的“0.01”和“1.01”必须是字符串
>>> Decimal("0.01") + Decimal("1.01")
Decimal('1.02')
>>>?
?
布尔值其实也是数字
布尔(bool)类型是Python里用来表示“真假”的数据类型,包括两个可选值:True和False。布尔值其实是整型的子类型,在绝大多数情况下,True和False可以当做1和0来使用。就像如下这样:
>>> int(True),int(False)
(1, 0)
>>> True + 1
2
>>> False +1
1
>>> 1 /True
1.0
# 把False当做除数,和0一样会抛出异常
>>> 1 /False
Traceback (most recent call last):
? File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>>?
利用“布尔值可作为整型使用”的特性,用一个简单的表达式实现统计一个包含整数列表中的偶数的数量:
>>> numbers = [1,2,3,4,5,6,7,8,9,10,11,12]
>>> count = sum(num % 2 == 0 for num in numbers)
>>> count
6
>>>?
特殊数字:“无穷大、无穷小”
Python中float("inf")和float("-inf")分别代表数学世界中的正负无穷大。
float("-inf")? < 任意数值 < float("inf")
比如有一个包含用户名和年龄的字典,我们需要把里面的用户名按照年龄升序排序,没有提供年龄的放在最后面。下面代码使用float("inf"),代码可以这么写:
def sort_users_inf(users):
def key_func(username):
age = users[username]
# 当年龄为空时,返回正无穷大作为key,因此会被排到最后
return age if age is not None else float("inf")
return sorted(users.keys(),key=key_func)
users = {"liuzx":19,"jenny":13,"jack":None,"liuyf":48}
print(sort_users_inf(users)) # 输出:['jenny', 'liuzx', 'liuyf', 'jack']
二、字符串的格式化
当前主流Python版本中,有3种主要的字符串格式化方式。
(1)C语言风格的基于百分号%的格式化语句:‘Hello, %s’? %? 'World'.
(2)新式字符串格式化(str.format)方式:"Hello ,{}".format("World")
(3)f-string字符串字面量格式化表达式(Python3.6新增):如下:
>>> name = "World"
>>> welcome_text = f"Hello,{name}"
>>> welcome_text
'Hello,World'
>>>
后两种方式越来越流行被采用,f-string格式化方式用起来最为方便,作者建议首选。
str.format 与f-string共享同一种复杂的“字符串格式化微语言”。通过这种微语言,可以方便地对字符串进行二次加工,然后输出。比如:
# 将username靠右对齐,左侧补齐空格到一共20个字符
# 下面两种方式将输出同样的内容
>>> username = "liuyf"
>>> print('{:>20}'.format(username))
? ? ? ? ? ? ? ?liuyf
>>> print(f'{username:>20}')
? ? ? ? ? ? ? ?liuyf
>>>?
一个应用案例:
字符串与字节串
(1)字符串:普通的字符串,也称为文本(text),是给人看的,对应Python中的字符串(str)类型。str使用Unicode标准,可以通过.encode()方法编码为字节串。
(2)字节串:有时也称为“二进制字符串”(binary string),是给计算机看的,对应Python中的字节串(bytes)类型。bytes一定包含某种真正的字符串编码格式(默认为UTF-8),可通过.decode()方法解码为字符串。
>>> str_obj = 'Hello,火星'
>>> str_obj
'Hello,火星'
>>> type(str_obj)
<class 'str'>
>>> bin_str = str_obj.encode('UTF-8')
>>> bin_str
b'Hello\xef\xbc\x8c\xe7\x81\xab\xe6\x98\x9f'
>>> type(bin_str)
<class 'bytes'>
>>>
bytes和str是两种不同的数据类型,即使有时看上去“一样”,但是做比较时永不相等:
>>> "Hello" == b"Hello"
False
>>>
另外,不能使用bytes来调用任何内置方法,反之亦然:
>>> "Hello".split("e")
['H', 'llo']
>>> "Hello".split(b"e")
Traceback (most recent call last):
? File "<stdin>", line 1, in <module>
TypeError: must be str or None, not bytes
>>>?
在程序中,尽量保证总是操作普通字符串,而非字节串。必须操作处理字节串的场景一般有两种:
(1)程序从文件或其它外部存储读取字节串内容,将其解码为字符串,然后在内部使用。
(2)程序完成处理,要把字符串写入文件或者其它外部存储,将其编码为字节串,然后继续执行其它操作。
小知识:普通字符串采用的是文本格式,没法直接存放于外部存储,一定要将其编码为字节串——也就是“二进制字符串”。比如一些文本编辑器编辑文本,我们看到是文本(普通字符串),当编辑器把文本作为文件保存到硬盘等存储器时,会进行编码,把文本及格式转换为字节串,才能保存到存储设备中。
改善代码的可读性案例
Python中没有真正的常量类型,一般把大写字母全局变量当做“常量”来用。比如把积分数定义为常量:
# 用户每日登录奖励积分数量
DAILY_POINTS_REWARDS = 100
# VIP用户每日登录,额外再奖励积分20
VIP_EXTRA_POINTS = 20
除了常量之外,Python3.4开始引入了专门用于表示枚举类型的内置模块,在程序中可以定义枚举类型(enum.Enum)作为常量:
from enum import Enum
# 在定义枚举类型时,如果同时继承一些基础类型,比如int,str
# 枚举类型就能同时充当该基础类型使用,必须下面代码,使用UserType就可以充当int使用
DIALY_POLINTS_REWARDS = 100
VIP_EXTRA_POLINTS = 20
class UserType(int,Enum):
# VIP 用户
VIP = 3
# 封禁的用户(黑名单用户)
BANNED = 13
def add_daily_points(user):
"""用户每天完成第一次登录后,为其增加积分
"""
# 用户为封禁用户
if user.type == UserType.BANNED:
return
# 用户为VIP用户
if user.type == UserType.VIP:
user.points += DIALY_POLINTS_REWARDS + VIP_EXTRA_POLINTS
return
# 普通用户
user.points = DIALY_POLINTS_REWARDS
return
使用常量和枚举变量类型的好处:
(1)代码更易读:所有人都不需要记忆某个数字代表什么。
(2)代码更健壮:降低输错数字或者字母产生bug的可能性。
改善超长字符串的可读性
为了保证代码的可读性,单行代码的长度不易过长,比如PEP8规范建议每行字符数不超过79,大部分人遵循不超过119个字符。遇到超长的字符串怎么办?
除了使用斜杠\和加号+将长字符串拆分为几段,还有一种更简单的方法,就是拿括号将长字符串包起来,之后就可以随意折行了。
s = ("This is the first line of a long string,"
"this is the second line."
)
print(s) # 输出:"This is the first line of a long string,this is the second line."
如果字符串出现在函数参数等位置,可以省略一层括号,下面语句输出和上面是一样的。
print("This is the first line of a long string,"
"this is the second line."
)