Python学习笔记¶

第三章¶

In [1]:
#复习
import time
import turtle
turtle.fd(100)
turtle.rt(90)
turtle.fd(100)
turtle.rt(45)
turtle.fd(140)
turtle.done()

3.1 数字类型及操作¶

3.1.1 整数类型¶

  • 可正可负,没有大小限制
  • pow(x,y) x的y次幂,没有大小限制。
In [4]:
print(pow(2,100))
print(pow(2,200))
print(pow(2,499))
# print(pow(2,pow(2,15))) 太长了,所以注释掉
1267650600228229401496703205376
1606938044258990275541962092341162602522202993782792835301376
1636695303948070935006594848413799576108321023021532394741645684048066898202337277441635046162952078575443342063780035504608628272942696526664263794688

可以看出,想算多大算多大。 提供了四种进制表示形式。

  • 十进制:1010 99 -114514
  • 二进制:0b01010 0B10101
  • 八进制:0O114514 0o1717610
  • 十六进制:0x114514 0X1f1fe10

3.1.2 浮点数类型¶

  • 与数学中实数概念一致。
  • 取值范围和小数精度都存在限制,但是常规计算可以忽略。
  • 浮点数运算存在不确定尾数。这不是bug。
In [7]:
print(0.1+0.3)
print(0.1+0.2)
print(0.1+0.2==0.3)
print(round(0.1+0.2,1)==0.3)
0.4
0.30000000000000004
False
True

53位二进制表示一个浮点数,由于二进制和十进制的差距,不是一一对应,所以会造成这种问题。 为解决此问题,可以使用round()函数。

  • round(x,d):对x四舍五入,截取d的位数。就是说保留几位小数?在这个基础上四舍五入。
  • 浮点数运算经常用round来辅助。不确定尾数经常发生在pow(10,-16)左右,因此round十分有效。

可以使用科学计数法来表示浮点数。

  • \<a>e\<b> 或者 \<a>E\<b> 表示a*pow(10,b)
  • 4.3e-3 表示 0.0043
  • 9.6E5表示960000.0

3.1.3 复数类型¶

  • pow(j,2) = -1
  • a+bj就是复数。a是实部,b是虚部。
In [8]:
z = 1.23e-4 + 5.6e+89j
print(z)
print(z.real) # 获得实部
print(z.imag) # 获得虚部
(0.000123+5.6e+89j)
0.000123
5.6e+89

3.1.4 数值运算操作符¶

  • x+y 加
  • x-y 减
  • x*y 乘
  • x/y 除,返回的结果是浮点数
  • x//y 整数除,返回的结果是整数 10//3=3
  • +x x本身
  • -y y的相反数
  • x%y 取余数
  • x**y 幂运算(y是小数的话开方)
In [11]:
x = 10//3
y = 2**10
z = -12
a = -z
b = +x
c = 10**0.5
d = pow(10,0.5) # pow和**相同
print(x,y,z,a,b,c,d)
3 1024 -12 12 3 3.1622776601683795 3.1622776601683795
  • x+=y x-=y x*=y x/=y x//=y x%=y x**=y
  • 二元操作符,结果给x
In [12]:
x = 3.1415
x**=3
print(x)
x = 3.1415
x=x**3
print(x)
31.003533398375005
31.003533398375005
  • 不同的类型之间可以互相运算,生成结果作为"最宽"的类型
  • 整数➡️浮点数➡️复数
In [13]:
print(123+4.0)
127.0

3.1.5 数值运算函数¶

  • abs(x) 绝对值
  • divmod(x,y) 结果为(商,余数)
  • pow(x,y[,z]) (x**y)%z
In [14]:
pow(3,pow(3,99),10000)
# pow(3,pow(3,99)) 很难算出来
Out[14]:
4587
  • round(x[,d]) 四舍五入,d默认为0,取整
  • max(a,b,...) 最大值
  • min(a,b,...) 最小值

类型转换相关的函数:

  • int(x) 把括号里的东西弄成整数,无论浮点数、字符串
  • float(x) 把括号里的东西弄成浮点数,无论整数、字符串
  • complex(x) 变成复数

3.2 代码实现1.01**365和0.99**365¶

In [2]:
# 1 一年365天,每天进步千分之一、退步千分之一。
dayup = pow(1.001,365)
daydown = pow(0.999,365)
print("向上:{:.2f}\n向下:{:.2f}".format(dayup,daydown))
# 2 千分之五呢?百分之一呢?其他呢?
dayfactor = 0.005
dayup = pow(1+dayfactor,365)
daydown = pow(1-dayfactor,365)
print("每天进步{:.3f}可以得到:{:.3f}\n每天退步{:.3f}可以得到:{:.3f}".format(dayfactor,dayup,dayfactor,daydown))
dayfactor = 0.01
dayup = pow(1+dayfactor,365)
daydown = pow(1-dayfactor,365)
print("每天进步{:.2f}可以得到:{:.3f}\n每天退步{:.3f}可以得到:{:.3f}".format(dayfactor,dayup,dayfactor,daydown))
# 3 工作日的力量
# 一年365天,每周2休息日,退步1%。
# 数学思维->计算思维
value = 1
factor_up = 0.01
factor_down = 0.01
for day in range(365):
    if day%7 in [0,6]: #周末
        value *= (1-factor_down)
    else:
        value *= (1+factor_up)
print('工作日的力量:{:.2f}'.format(value))
# 4 工作日的努力
# 在工作日,要努力到什么水平,才能和每天1%达到一样的成绩?
# 自己写个函数
def weekday_method(factor_up,factor_down):
    value = 1
    for day in range(365):
        if day%7 in [0,6]:
            value *= (1-factor_down)
        else:
            value *= (1+factor_up)
    return value

factor_up = 0.01
factor_down = 0.01
while weekday_method(factor_up,factor_down) < 37.78:
    factor_up+=0.001
print('所需的努力参数是:{:.3f}'.format(factor_up))
向上:1.44
向下:0.69
每天进步0.005可以得到:6.175
每天退步0.005可以得到:0.160
每天进步0.01可以得到:37.783
每天退步0.010可以得到:0.026
工作日的力量:4.63
所需的努力参数是:0.019

抽象和自动化是重要的思维。 上面的代码涉及到了while循环、函数、分支等内容。

3.3 字符串类型和操作¶

3.3.1 字符串类型的表示¶

在第一章的时候曾经学习过:

  • 字符串:
    • 用单引号或者双引号来表达,都一样
    • 由0个或多个字符组成的有序序列
    • 可以用顺序去做索引,从0开始计数
    • image.png
  • 字符串的索引和切片
    • 索引:返回单个字符 <字符串>[N]
      • TempStr[0]
      • "请输入字符串"[2] 表达'入'
    • 切片:返回一个子串 <字符串>[M:N] 从M到N-1!
      • TempStr[0:-1] 从0开始到-2,即不包含最后一个字符,去掉最后一个字符
      • "请输入字符串"[2:4] 表达"入字",但不包含"字" 现在要拓展一下,更加高级的知识:
  • 由单引号或者双引号表示的是单行字符串
  • 三单引号或者三双引号表示多行字符串
In [ ]:
'''你好,
今晚"'月色'真美"。'''

并不是说三单引号真的能成为注释,而是三单引号可以起到这样的作用。 Python一个人性化的一点是尽可能写的时候舒服点

In [ ]:
'这里面有个双引号"'
"这里面有个单引号'"
  • 字符串切片的高级用法
    • [M:N:K]表示根据步长来进行切片。
    • M可以缺失,表示从开头
    • N可以缺失,表示到结尾
  • 高级用法:
    • 逆序字符串[::-1]
    • K为负,表示从后往前输出
In [5]:
test1 = '〇一二三四五六七八九十'
print(test1[:3]) #从开头到第3序号(不含3)
print(test1[1:8:2]) #从1号到8(不含8),间隔2
print(test1[::-1])
〇一二
一三五七
十九八七六五四三二一〇
  • 转义字符
    • 用\来开头
    • \n表示换行
    • \"可以在字符串里插入一个双引号

3.3.2 字符串的操作符¶

  • x+y 拼接字符串
  • nx或xn 复制n次字符串x
  • x in s x是不是s的子串
In [6]:
#输入3
weekStr = "星期一星期二星期三星期四星期五星期六星期日"
weekNum = eval(input("请输入您需要星期几?(数字1-7)"))
weekPos = (weekNum-1)*3
print(weekStr[weekPos:weekPos+3])
星期三
In [7]:
#输入3
weekStr = "一二三四五六七日"
weekNum = eval(input("请输入您需要星期几?(数字1-7)"))
weekPos = (weekNum-1)
print("星期"+weekStr[weekPos])
星期三

3.3.3 字符串处理函数¶

  • len(x) 返回字符串长度
    • 不论汉字、数字、英文、emoji,都是1
  • str(x) 把x变成字符串
    • 这与eval恰好是相反的
  • hex(x) oct(x) 整数x的十六进制或者八进制小写字符串
  • chr(u) unicode编码➡️字符
  • ord(x) 字符x➡️unicode编码
    • 所有python字符串都是unicode编码
In [11]:
print(len("😅"))
print(str([1,2]))
print("1+1=2" + chr(10004))
print(str(ord("😅")))
for i in range(12):
    print(chr(9800+i), end="") #中间没有间隔符号
1
[1, 2]
1+1=2✔
128517
♈♉♊♋♌♍♎♏♐♑♒♓

3.3.4 字符串处理方法¶

  • 方法:字符串所提供的函数,即字符串对象的函数
  • str.lower() str.upper() 返回字符的副本,全部大写/小写
  • str.split(sep=None) 返回一个列表,str根据sep进行分隔
  • str.count(sub) 返回sub子串在str中出现的次数
  • str.replace(old,new) 返回str的副本,所有old子串被替换为new
  • str.center(width[,fillchar]) 返回副本,居中str,并使用fillchar填充
  • str.strip(chars) 返回副本,去除左、右的在chars中出现的所有字符
  • str.join(iter) 在iter中,除了最后一个字符,每个字符后面都加一个str。常用于分隔字符串。
In [18]:
str = "A,B,C,OLG"
print(str.split(','))
str = "ababa"
print(str.count('aba'))
str = "十是十,四是四,十四是十四,四十是四十"
print(str.replace('四','4'))
print(str.center(50,'*'))
print(str) # 注意这些操作都不会改变str本身
str = '= python ='
print(str.strip(' =np'))
str = ' and '
print(str.join("一二三四五"))
['A', 'B', 'C', 'OLG']
1
十是十,4是4,十4是十4,4十是4十
***************十是十,四是四,十四是十四,四十是四十****************
十是十,四是四,十四是十四,四十是四十
ytho
一 and 二 and 三 and 四 and 五

3.3.5 字符串类型的格式化¶

就是类似于C语言的printf那些东西。

  • 字符串.format()
  • str.format(逗号分隔的参数)
  • 槽:{}
    • 默认按照0,1,2,...编号
    • format中参数的顺序也是一致的
    • 也可以给槽手动编号,表示对应第几个参数
In [23]:
str = "{}:计算机{}的CPU占用率为{}%".format("2022-02-12","MacBook Pro 18,3",8)
print(str)
str = "{1}是一门{0},{1}很好用。".format("编程语言","Python")
print(str)
2022-02-12:计算机MacBook Pro 18,3的CPU占用率为8%
Python是一门编程语言,Python很好用。
  • 槽内部进行格式控制
  • {<参数序号>:<格式控制标记>}
    • 一共有6个格式控制标记
  • 填充、对齐和宽度
    • 给输出宽度、对齐方式、多了的怎么填充
    • 冒号后,第一个字符是填充内容
    • 第二个字符 < 左对齐 ^ 居中对齐 > 右对齐
    • 之后的数字表示宽度
In [30]:
str = "{0:=^30}".format("python")
print(str)
str = "{0:=>20}".format("python")
print(str)
str = "{0:=<20}".format("python")
print(str)
============python============
==============python
python==============
  • 千位分隔符、精度、类型
    • ,表示数字的千位分隔符
    • .精度 浮点数小数精度或者字符串最长输出长度
    • 类型
      • 整数类型b c d o x X
      • 分别是二进制、unicode字符、十进制、八进制、小写/大写十六进制
      • 浮点数类型 e E f %
      • 分别是小写/大写科学计数法、普通浮点数、百分数
In [33]:
str = "{0:=^20,.6f}".format(114514.1919810)
print(str)
str = "{0:b},{0:c},{0:d},{0:o},{0:x},{0:X}".format(425)
print(str)
str = "{0:e},{0:E},{0:f},{0:%}".format(1.14514)
print(str)
===114,514.191981===
110101001,Ʃ,425,651,1a9,1A9
1.145140e+00,1.145140E+00,1.145140,114.514000%

3.4 time库的使用¶

Python中的标准库,import time即可。 可以提供系统级的计时、时间格式化等功能。

3.4.1 时间获取¶

  • time() 获取时间戳 从1970-1-1 0:00 开始的过了多少秒。GMT标准时间。
  • ctime() 获取时间字符串,比较好读
  • gmtime() 生成一个时间结构体
In [37]:
import time
print(time.time())
print(time.ctime())
print(time.gmtime())
1644638449.641355
Sat Feb 12 12:00:49 2022
time.struct_time(tm_year=2022, tm_mon=2, tm_mday=12, tm_hour=4, tm_min=0, tm_sec=49, tm_wday=5, tm_yday=43, tm_isdst=0)

3.4.2 时间格式化¶

让时间按照我们希望的格式来输出,也需要格式控制字符串。

  • strftime(tpl,ts)
    • tpl是格式化模板字符串
    • ts是内部时间类型变量
In [38]:
t = time.gmtime()
print(time.strftime("%Y-%m-%d %H:%M:%S",t))
2022-02-12 04:00:52

看看控制符:

  • %Y 年份 0000-9999
  • %m 月份 01-12
  • %B 英文月份全称,如September
  • %b 英文月份简称,如Sep
  • %d 日期 01-31
  • %A 星期全称,如Monday
  • %a 星期简称,如Mon
  • %H 24小时制的小时
  • %I 12小时制的小时
  • %p 上午/下午,AM/PM
  • %M 分钟,与%m月份区别开
  • %S 秒钟

我们也可以通过strptime()通过时间字符串构造成一个时间变量。

  • strptime(时间字符串,模板字符串)
    • str 时间字符串
    • tpl 模板字符串
In [39]:
timeStr = "2022-02-12 04:00:52"
print(time.strptime(timeStr,"%Y-%m-%d %H:%M:%S"))
time.struct_time(tm_year=2022, tm_mon=2, tm_mday=12, tm_hour=4, tm_min=0, tm_sec=52, tm_wday=5, tm_yday=43, tm_isdst=-1)

3.4.3 程序计时应用¶

看看一个程序从开始到结束经历了多少时间。

  • 测量时间 perf_counter() 精准,可以到纳秒的精度
    • 需要连续调用取差值,才是真正的运行时间
  • sleep()
In [43]:
start = time.perf_counter()
print(start)
for i in range(1919810):
    ;
end = time.perf_counter()
print(end)
print(end-start)
192355.968274708
192356.017365416
0.04909070799476467
  • sleep(s)
    • s单位是秒,让程序拟休眠的时间
    • time.sleep(3.3) 让程序休眠3.3秒
In [44]:
start = time.perf_counter()
time.sleep(5)
end = time.perf_counter()
print(end-start)
5.00568583299173

3.5 实例:文本进度条¶

用字符打印的方式来实现一个文本的进度条,在一行中逐渐变化。 用sleep()模拟一个持续的进度。

3.5.1 简单的开始¶

In [48]:
scale = 10
print("{0:=^20}".format("执行开始"))
for i in range(scale+1): #从0到10,11次循环
    a = '*' * i
    b = '.' * (scale-i)
    c = (i/scale)*100
    print("{:^3.0f}%[{}->{}]".format(c,a,b))
    time.sleep(0.2)
print("{0:=^20}".format("执行结束"))
========执行开始========
 0 %[->..........]
10 %[*->.........]
20 %[**->........]
30 %[***->.......]
40 %[****->......]
50 %[*****->.....]
60 %[******->....]
70 %[*******->...]
80 %[********->..]
90 %[*********->.]
100%[**********->]
========执行结束========

3.5.2 实现单行动态刷新¶

  • 即用之后的字符覆盖原来的字符
  • 需要不换行(end="")、回退(\r)
In [51]:
#从0到100的动态刷新
for i in range(101):
    print("\r{:3}%".format(i),end="")
    time.sleep(0.1)
100%

3.5.3 完整效果¶

In [60]:
scale = 50 # 迭代50次
width = 15 # 20+->,共22
print("执行开始".center(scale//2,"*"))
start = time.perf_counter() # 实现计时效果
for i in range(scale+1):
    progress = (i / scale)
    a = '▶️' * (round(width*progress))
    b = '⏺' * (width - round(width*progress))
    end = time.perf_counter()
    duration = end - start
    print("\r{:.3%} [{}->{}] duration:{:.3f}".format(progress,a,b,duration),end="")
    time.sleep(0.2)
print("\n执行完毕".center(scale//2,"*"))
***********执行开始**********
100.000% [▶️▶️▶️▶️▶️▶️▶️▶️▶️▶️▶️▶️▶️▶️▶️->] duration:10.214**********
执行完毕**********

应该在长时间运行的程序中加入进度条,这也能够提升人们的体验。 当然,这涉及到一些有些有趣的心理学问题,比如前面增长的很快,后面很慢,这样就不太符合人的直观感受。 有相关的研究,开始展示的慢一些,后面越来越快,这样更符合人们的心理感受。