Python零基础速成班-第11讲-Python日志Logging,小游戏设计game of life

学习目标

  1. Python日志Logging
  2. 小游戏设计game of life
  3. 课后作业(2必做)

友情提示:将下文中代码拷贝到JupyterNotebook中直接执行即可,部分代码需要连续执行。

1、日志Logging

日志是对软件执行时所发生事件的一种追踪方式。软件开发人员对他们的代码添加日志调用,借此来指示某事件的发生。一个事件通过一些包含变量数据的描述信息来描述(比如:每个事件发生时的数据都是不同的)。开发者还会区分事件的重要性,重要性也被称为 等级 或 严重性。

参考文档:https://docs.python.org/zh-cn/3/howto/logging.html#logging-basic-tutorial

对于简单的日志使用来说日志功能提供了一系列便利的函数。它们是 debug(),info(),warning(),error() 和 critical()。想要决定何时使用日志,请看下表,其中显示了对于每个通用任务集合来说最好的工具。

日志功能应以所追踪事件级别或严重性而定。各级别适用性如下(以严重性递增):

级别 何时使用
DEBUG 细节信息,仅当诊断问题时适用。
INFO 确认程序按预期运行。
WARNING 表明有已经或即将发生的意外(例如:磁盘空间不足)。程序仍按预期进行。
ERROR 由于严重的问题,程序的某些功能已经不能正常执行
CRITICAL 严重的错误,表明程序已不能继续执行

1.1 一个非常简单的例子:

INFO 消息并没有出现,因为默认日志追踪级别的阈值是 WARNING ,即高于等于该级别(WARNING/ERROR/CRITICAL)才被输出。

import logging
def log():logging.warning('Watch out!')  # will print a message to the consolelogging.info('I told you so')  # will not print anything
log()
WARNING:root:Watch out!

请确认启动新的Python 解释器或者清空Jupyter notebook,不要在上一个环境中继续操作,因为logging.basicConfig是全局设置!

1.2 在Python3.9版本增加了 encoding 参数,下例中展示了日志追踪级别的阈值,我们设置的阈值是 DEBUG,所有信息都将被打印。

import logging
logging.basicConfig(encoding='utf-8', level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
logging.error('And non-ASCII stuff, too, like Øresund and Malmö')
DEBUG:root:This message should go to the log file
INFO:root:So should this
WARNING:root:And this, too
ERROR:root:And non-ASCII stuff, too, like Øresund and Malmö

1.3 更改显示消息的格式。

  1. %(asctime)s 日期时间
  2. %(levelname)s 消息级别
  3. %(message)s 消息内容

下例中包含debugging日志方便调试处理

import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s -  %(levelname)s-  %(message)s')
logging.debug('Start of program')def factorial(n):logging.debug('Start of factorial(%s)'  % (n))total = 1for i in range(n + 1):total *= ilogging.debug('i is ' + str(i) + ', total is ' + str(total))logging.debug('End of factorial(%s%%)'  % (n))return totalprint(factorial(5))
logging.debug('End of program')
2022-05-31 16:10:38,132 -  DEBUG-  Start of program
2022-05-31 16:10:38,135 -  DEBUG-  Start of factorial(5)
2022-05-31 16:10:38,136 -  DEBUG-  i is 0, total is 0
2022-05-31 16:10:38,137 -  DEBUG-  i is 1, total is 0
2022-05-31 16:10:38,138 -  DEBUG-  i is 2, total is 0
2022-05-31 16:10:38,138 -  DEBUG-  i is 3, total is 0
2022-05-31 16:10:38,138 -  DEBUG-  i is 4, total is 0
2022-05-31 16:10:38,139 -  DEBUG-  i is 5, total is 0
2022-05-31 16:10:38,140 -  DEBUG-  End of factorial(5%)
2022-05-31 16:10:38,141 -  DEBUG-  End of program

1.4如果你的程序包含多个模块,这里有一个如何组织日志记录的示例:

import logging
import time
def sum_it():total = 0for i in range(5000000):total += iprint(total)
def main():logging.basicConfig(encoding='utf-8',level=logging.INFO)logging.info(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())+' sum_it Started')sum_it()logging.info(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())+' sum_it Finished')if __name__ == '__main__':main()
2022-05-31 16:10:40,415 -  INFO-  2022-05-31 16:10:40 sum_it Started
2022-05-31 16:10:40,704 -  INFO-  2022-05-31 16:10:40 sum_it Finished12499997500000

1.5 将日志写入文件,filename为存放日志的位置和文件名。

如下例,我们将日志文件写入绝对地址D://myapp.log文件中,当有日志产生时,则会自动写入该文件。

import logging
logging.basicConfig(filename='D://myapp.log',encoding='utf-8', level=logging.DEBUG, format='%(asctime)s -  %(levelname)s -  %(message)s')
try:fh = open("D:/dal/test/errorInfo.txt", "w")fh.write("这是一个测试文件,用于测试异常!!")
except IOError:logging.warning("Error: 没有找到文件或读取文件失败")
2022-05-31 16:10:47,982 -  WARNING-  Error: 没有找到文件或读取文件失败

1.6 日志禁用功能。

disable()语法是禁用它之后的所有日志消息,我们可以将其添加到程序中代码的导入日志行附近。这样就可以很容易地找到注释或取消注释并根据需要启用或禁用日志消息的调用。

import logging
logging.basicConfig(level=logging.INFO, format=' %(asctime)s -%(levelname)s -  %(message)s')
logging.critical('Critical error! Critical error!')logging.disable(logging.CRITICAL)
logging.critical('Critical error! Critical error!')
logging.error('Error! Error!')
2022-05-31 16:12:10,334 -  CRITICAL-  Critical error! Critical error!

2、小游戏设计game of life

game of life,我们简称为生命游戏,是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。它最初于1970年10月在《科学美国人》杂志中马丁·葛登能(Martin Gardner,1914年11月21日-2010年5月22日。又译:马丁·加德纳)的“数学游戏”专栏出现。

给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞(cell)。每个细胞都具有一个初始状态: 1 即为活细胞(live),或 0 即为死细胞(dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:

1. 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;(人口过少)
2. 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;(正常)
3. 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;(人口过多)
4. 如果死细胞周围正好有三个活细胞,则该位置死细胞复活。(起死回生)

可以把最初的细胞结构定义为种子,当所有在种子中的细胞同时被以上规则处理后,可以得到第一代细胞图。按规则继续处理当前的细胞图,可以得到下一代细胞图,周而复始。

如下例:

第一代细胞:
[[0,1,0],[0,0,1],[1,1,1],[0,0,0]
]第二代细胞:
[[0,0,0],[1,0,1],[0,1,1],[0,1,0]
]

2.1 代码实现

第一步,构建一个细胞Cell和8个邻居neighbors

def neighbors(cell):(x,y) = cellreturn [(x-1,y-1),(x,y-1),(x+1,y-1),(x-1,y),             (x+1,y),(x-1,y+1),(x,y+1),(x+1,y+1)]

第二步,根据当前世界,得到每个细胞Cell的邻居数量

首先定义最初代的细胞,即种子world
接下来再统计每个细胞的存活的邻居的数量
(2, 1): 2,表明(2, 1)是两个alive cell的邻居,意思也是(2, 1)有两个alive cell邻居

world={(3,1),(1,2),(1,3),(2,3)}
from collections import Counter#引入统计包
def neighbors_counts(world):#统计周围邻居数量return Counter(live for cell in world#相当于一个嵌套循环,先把存活细胞循环出来,再统计每个细胞的存活的邻居的数量for live in neighbors(cell))
neighbors_counts(world)#输出每个细胞的邻居数量
Counter({(2, 0): 1,(3, 0): 1,(4, 0): 1,(2, 1): 2,(4, 1): 1,(2, 2): 4,(3, 2): 2,(4, 2): 1,(0, 2): 2,(1, 2): 2,(0, 3): 2,(2, 3): 2,(0, 4): 1,(1, 4): 2,(2, 4): 2,(0, 1): 1,(1, 1): 1,(1, 3): 2,(3, 3): 1,(3, 4): 1})

第三步,根据当前世界得到下一代的结果,主程序完成

这里只需要做两种判断即可:

  1. 当细胞周围邻居数量是3时,均存活。
  2. 当细胞周围邻居数量是2,且细胞本身是存活状态,则细胞继续存活。
def next_generation(world):cells = counts = neighbors_counts(world) return { cell for cell in cellsif(counts[cell]==3)#不管alive or dead ,均存活or(counts[cell]==2 and cell in world)#是周围2个alive cell且自己也是alive cell}
next_generation(world)
{(1, 2), (1, 3), (2, 3)}

第四步,更多次的演化

通过next_generation(world)函数不断迭代演化

def run (world,num:"次数"):for n in range(num):world = next_generation(world)return world
run({(3,1),(1,2),(1,3),(2,3)},5)
{(1, 2), (1, 3), (2, 2), (2, 3)}

第五步,显示出来

首先定义画布大小为:dx × dy 个格子的面板,x为横轴,y为竖轴。存活用 @ 表示,死亡用 . 表示。
再嵌套遍历y和x轴,分别标识存活和死亡细胞的位置坐标并输出。

alive = "@"
dead = "."
space =" "
def display(world,dx,dy):#dx为横向输出,dy为竖向输出def row(y):return space.join(alive if(x,y)in world else dead for x in range(dx))return "\n".join(row(y) for y in range(dy))
print(display({(1, 2), (1, 3), (2, 2), (2, 3)},5,5))
. . . . .
. . . . .
. @ @ . .
. @ @ . .
. . . . .

第六步,连续显示出来

world为当前存活细胞,num为迭代次数,pause为画面暂停时间(这里我们使用time.sleep()方法使时间暂停)。
其中clear_output()方法的作用是清空当前输出,每迭代演化一次,输出最新演化后的图形,直至全部迭代次数执行完毕。

from IPython.display import clear_output
import time
def display_run(world,num:"次数",dx,dy,pause:"画面暂停时间"=1):for n in range(num):clear_output()#清空当前输出print("迭代演化次数:{}\n{}".format(n+1,display(world,dx,dy)))#Generation迭代次数time.sleep(pause)world = next_generation(world)
display_run({(3,1),(1,2),(1,3),(2,3),(4,2),(5,4),(5,2)},10,10,10)
迭代演化次数:10
. . . . . . . . . .
. . . . . . . . . .
. @ @ . . . . . . .
. @ @ . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .

3、课后作业,答案在下一讲

1、写一个计算程序,计算一个list列表[3,5,0,4.5,0.8,7,14]的第i项元素与第j项元素相除的结果,使用try/catch,对可能遇到除数为0,i,j超出数组界限等错误进行输出。不断完善你的程序,使其可以正常执行不报错。i,j通过input()输入。

您的代码:

2、设计一个函数,统计并输出0至n相加之和,n由input输入。在函数执行前输出INFO日志(“日期时间—日志级别—统计0至n相加之和程序开始!”)当n > 100万时,输出ERROR日志(“日期时间—日志级别—n大于100万!”),仅输出日志不影响程序。

您的代码:

4、上一讲Python零基础速成班-第10讲-Python面向对象编程(下),Property属性、特殊方法、设计模式、链表应用 课后作业及答案

1、使用以下属性创建名为Point的类:

x:float
y:float

包含以下四种方法:

 __str__():返回字符串,如:(1.0,2.0)__repr__():返回字符串,如:Point(1.0,2.0)get_x:返回x值get_y:返回y值
class Point():def __init__(self,x:float,y:float):self.x = xself.y = ydef __str__(self):return str((self.x,self.y))def __repr__(self):return "Point"+str(self)def get_x(self):return self.xdef get_y(self):return self.y    p = Point(1.0,2.0)
print(p)
p
(1.0, 2.0)Point(1.0, 2.0)

2、使用以下属性创建名为Circle的类,继承Point:

center:Point
radius:float

包含以下两种方法:

__str__():返回字符串,如:Circle at(1.0,2.0),r = 5.0
__repr__():返回字符串,如:Circle(center=Point(1.0,2.0),r = 5.0)
class Circle(Point):def __init__(self,center,radius):self.point = centerself.radius = radiusdef __str__(self):return "Circle at"+str(self.point)+"r="+str(self.radius)def __repr__(self):return "Circle(center=Point"+str(self.point)+",r="+str(self.radius)+")"
c=Circle(center=Point(1.0,2.0),radius=5.0)
print(c)
c
Circle at(1.0, 2.0)r=5.0Circle(center=Point(1.0, 2.0),r=5.0)

3、使用以下属性创建名为Triangle的类,继承Point:

vertx:tuple of Point

包含以下两种方法:

__str__():返回字符串,如:Triangle at((0.0,0.0),(0.0,10.0),(5.0,5.0))
__repr__():返回字符串,如Triangle(vertx=(Point(0.0,0.0),Point(0.0,10.0),Point(5.0,5.0)))

和计算方法:

area:area= ∣Ax(By−Cy)+Bx(Cy−Ay)+Cx(Ay−By)2∣\left| \frac{A_x(B_y-C_y)+B_x(C_y-A_y)+C_x(A_y-B_y)}{2} \right|∣∣∣​2Ax​(By​−Cy​)+Bx​(Cy​−Ay​)+Cx​(Ay​−By​)​∣∣∣​

提示:

__str__()方法可以使用语法:"Triangle at(%s,%s,%s)" %self.vertx
class Triangle(Point):def __init__(self,vertx:"puple"):self.vertx = vertxdef __str__(self):return "Triangle at(%s,%s,%s)" %self.vertxdef __repr__(self):return "Triangle(vertx="+str(self.vertx)+")"def area(self):#继承了Point对象,可以直接使用get方法A,B,C=self.vertxAx=A.get_x()Ay=A.get_y()Bx=B.get_x()By=B.get_y()Cx=C.get_x()Cy=C.get_y()return abs((Ax*(By-Cy)+Bx*(Cy-Ay)+Cx*(Ay-By))/2)
t = Triangle(vertx=(Point(0.0,0.0),Point(0.0,10.0),Point(5.0,5.0)))
print(t)
print(repr(t))
t.area()
Triangle at((0.0, 0.0),(0.0, 10.0),(5.0, 5.0))
Triangle(vertx=(Point(0.0, 0.0), Point(0.0, 10.0), Point(5.0, 5.0)))25.0

*(扩展)4、编程实践项目

模拟计算器的操作输出(从小学起大家就知道,先乘除,再加减,有括号先算括号里的内容,那么计算机是如何实现的呢?)

如下例,我们如何实现下面计算式在计算机中的运算呢?我们首先需要将中缀运算式转化为计算机可以执行的后缀运算式。

2 - (5 + 3$*$2 +1) / 3

简单介绍一下前缀、中缀、后缀表达式

  1. 前缀表达式(Prefix Notation)是指将运算符写在前面操作数写在后面的不包含括号的表达式,而且为了纪念其发明者波兰数学家Jan Lukasiewicz(卢卡西维兹发明于1929),所以前缀表达式也叫做“波兰表达式”。
  2. 后缀表达式(Postfix Notation)与之相反,是指运算符写在操作数后面的不包含括号的算术表达式,也叫做逆波兰表达式。
  3. 中缀表达式(Infix Notation)就是常用的将操作符放在操作数中间的算术表达式。前缀表达式和后缀表达式相对于中缀表达式最大的不同就是去掉了表示运算符优先级的括号。

任务第一步:将中缀转化为后缀

模拟一下中缀转后缀过程:从左到右遍历中缀表达式,数字则输出,成为后缀表达式的一部分,若是遇到符号,判断其余栈顶符号的优先级,是有括号或优先级不高于栈顶符号则栈顶元素依次出栈并输出,如下例:

2 - (5 + 3*2 +1) / 3
2
2 [-]
2 [-,(]
25 [-,(]
25 [-,(,+]
253 [-,(,+]
253 [-,(,+,*]
2532 [-,(,+,*]
2532*+ [-,(,+]
2532*+1+ [-]
2532*+1+ [-,/]
2532*+1+3 [-,/]
2532*+1+3/-
def infix_to_postfix(infix:str)->str:infix= "".join([c for c in infix if c != " "])#首选去掉空格print(infix)stack =[]out =''for c in infix:#pop digitif c.isdigit():out+=celif c in '+-*/':#pop operation with higher precedencehighop = "*/+-" if c in"+-" else "*/"#+-输出*/+-,*/输出*/while stack and stack[-1] in highop:#不包含左括号"("out += stack.pop()#取出栈顶stack.append(c)elif c == "(":#左"("直接入栈stack.append(c)else:#  只剩下")"这种类型while stack and stack[-1] != '(':out+=stack.pop()stack.pop()#栈中最后去掉"("while stack:out+=stack.pop()#把最后剩下的符号依次出栈return out
infix_to_postfix("2 - (5 + 3*2 +1) / 3")
2-(5+3*2+1)/3'2532*+1+3/-'

任务第二步:计算后缀字符串

模拟一下后缀字符串计算过程 从左到右遍历后缀表达式,遇到数字就进栈,遇到是符号,就将处于栈顶两个数字出栈,进行运算,运算结果进栈,一直到最终获得结果,如下例:

2532*+1+3/-
[2]
[2,5]
[2,5,3]
[2,5,3,2]*
[2,5,6]+
[2,11]
[2,11,1]+
[2,12]
[2,12,3]/
[2,4]-
[-2]
def eval_postfix(postfix:str)->"最终结果":def compute(op,op1,op2):if op=="+":return op1+op2elif op=="-":return op1-op2elif op=="*":return op1*op2else:return op1/op2stack=[]postfix= "".join([c for c in postfix if c != " "])#首选去掉空格for c in postfix:if c.isdigit():#当是数字时,直接转化为数字入栈stack.append(int(c))else:#当是运算符时,取出栈顶两个元素,进行运算,将结果入栈op2=stack.pop()op1=stack.pop()stack.append(compute(c,op1,op2))return stack.pop()#栈最后剩下的就是最终结果
eval_postfix("2532*+1+3/-")
-2.0

Python零基础速成班-第11讲-Python日志Logging,小游戏设计game of life相关推荐

  1. Python零基础速成班-第12讲-Python获取网络数据Socket,API接口,网络爬虫Crawler(制作弹幕词云)

    Python零基础速成班-第12讲-Python获取网络数据Socket,API接口,网络爬虫Crawler(制作弹幕词云) 学习目标 获取网络数据Socket API接口 网络爬虫Crawler(制 ...

  2. Python零基础速成班-第9讲-Python面向对象编程(上),对象和类、初始化、继承、重写、多态、类方法、组合

    Python零基础速成班-第9讲-Python面向对象编程(上),对象和类.初始化.继承.重写.多态.类方法.组合 学习目标 修饰器 面向对象编程:对象和类.初始化.继承.重写.多态.类方法.组合 课 ...

  3. Python零基础速成班-第10讲-Python面向对象编程(下),Property属性、特殊方法、设计模式、链表应用

    Python零基础速成班-第10讲-Python面向对象编程(下),Property属性.特殊方法.设计模式.链表应用 学习目标 面向对象编程 接上一讲:Property属性.特殊方法.设计模式 面向 ...

  4. Python零基础速成班-第5讲-Python函数,Function和Lambda基础

    Python零基础速成班-第5讲-Python函数,Function和Lambda基础 学习目标 Function函数 Lambda Function函数 课后作业(4必做) 友情提示:将下文中代码拷 ...

  5. Python零基础速成班-第6讲-Python异常处理Exception,tryexcept,raise,assert,输入模块pyinputplus

    Python零基础速成班-第6讲-Python异常处理Exception,try&except,raise,assert,输入模块pyinputplus 学习目标 异常处理Exception: ...

  6. Python零基础速成班-第8讲-Python文件操作File IO、高级文件处理模块shutil、CSV、JSON、多线程基础

    Python零基础速成班-第8讲-Python文件操作File I&O.高级文件处理模块shutil.CSV.JSON.多线程基础 学习目标 文件操作File I/O 高级文件处理模块shut ...

  7. Python零基础速成班-第14讲-Python处理Excel和Word,使用openpyxl和docx包详解,图表入门

    Python零基础速成班-第14讲-Python处理Excel和Word,使用openpyxl和docx包详解,图表入门 学习目标 Python处理Excel(使用openpyxl包).图表入门\ P ...

  8. Python零基础速成班-第13讲-Python正则表达式Regex

    Python零基础速成班-第13讲-Python正则表达式Regex 学习目标 正则表达式 课后作业(4必做) 友情提示:将下文中代码拷贝到JupyterNotebook中直接执行即可,部分代码需要连 ...

  9. Python零基础速成班-第2讲-Python基础(上),运算、变量、数据类型、输入输出

    Python零基础速成班-第2讲-Python基础(上),运算.变量.数据类型.输入输出 学习目标 使用print输出结果 运算及运算符 变量 数据类型(4种最常用的) 输入输出 课后作业(4必做+1 ...

最新文章

  1. 浅淡Webservice、WSDL三种服务访问的方式(附案例)
  2. MongoDB优越性
  3. anaconda moviepy_Win10配置anaconda和jupyter
  4. Laravel笔记记录
  5. 1.1.1.1校园网_Apache Flink 1.11.0 重要功能全面解析
  6. java自定义 filter,HBase自定义Filter
  7. Maven实现热部署需要的依赖
  8. 三星官方确认:vivo将首发搭载Exynos 1080旗舰芯片
  9. 【mysql union all limit的使用】
  10. 5、UmbracoNewsSite:添加css和js文件
  11. 计算机等级考试二级要学PS,关于计算机等级考试题库你所不知道的那些一级PS操作题...
  12. 魔兽怀旧服怎么找不到服务器,魔兽世界怀旧服世界服务器无法连接怎么办
  13. Canvas API(画布)简介
  14. Android Manifest配置文件中use-permission相关内容列表
  15. 苹果macOS Big Sur 11.2 RC 修复蓝牙和显示连接问题
  16. 聚苯乙烯核-聚(丙烯酰胺-丙烯酸)壳荧光素微球/磺酸官能化聚苯乙烯高荧光微球的制备
  17. linux内核启动过程3:内核初始化阶段
  18. 如何将Mac文件添加到 iCloud 云盘的具体操作方法!
  19. java 对某个文件改名_java中给文件改名
  20. 解决ERROR in ./node_modules/vuex/dist/vuex.esm.js

热门文章

  1. python去掉拼音的音调
  2. Linux学习之路--基础命令(4)
  3. 计算机开机内存占用80%,Win10开机后内存占用高80%以上怎么办_win10电脑开机内存占用高达80%以上如何解决...
  4. Linux 平台下zRAM 和 swap 使用(内存交换)
  5. 数据类型及常量和变量的一些基础知识
  6. Learning to Communicate with Deep Multi-Agent Reinforcement Learning笔记
  7. oracle实例注册监听,Oracle 19C 监听无法动态注册实例
  8. MVC辅助器方法之内联、外部辅助器方法
  9. ViewPager实现动画从入门到精通(二)---JazzyViewPager使用
  10. 调用手机数字键盘,QQ浏览器兼容性(输入一个数字,键盘就会隐藏的问题)