列表与*操作 python中,*操作符与list结合使用,实现元素复制
复制10个|字符:
1 2 >>> ['|' ] * 10 ['|' , '|' , '|' , '|' , '|' , '|' , '|' , '|' , '|' , '|' ]
复制5个空列表:
1 2 >>> [[]] * 5 [[], [], [], [], []]
a中的元素有一个空的list,a的长度为5,使用*复制
1 2 3 >>> a = [[]] * 5 >>> a[[], [], [], [], []]
根据业务规则,如下填充元素
1 2 >>> a[0 ].extend([1 ,3 ,5 ])>>> a[1 ].extend([2 ,4 ,6 ])
预期结果和实际结果
- 实际结果,a填充的结果为
1 2 >>> a[[1 , 3 , 5 , 2 , 4 , 6 ], [1 , 3 , 5 , 2 , 4 , 6 ], [1 , 3 , 5 , 2 , 4 , 6 ], [1 , 3 , 5 , 2 , 4 , 6 ], [1 , 3 , 5 , 2 , 4 , 6 ]]
原来*操作复制出的a[0]、a[1]、…、a[5],在内存中标识符是相等的,实现的仅仅是浅复制。
1 2 3 4 5 6 7 8 9 10 >>> a = []>>> a = [[]] * 5 >>> a[[], [], [], [], []] >>> id (a[0 ])4504403520 >>> id (a[1 ])4504403520 >>> id (a[2 ])4504403520
这种情况,就是修改其中一个元素会影响到其他的元素,那么如果要互补干扰,怎么做呢?那就要id[0]和id[1]不相等。不使用*,使用列表生成式,复制出5个不同id的内嵌列表,这样就能避免复制互不干扰的问题。
1 2 3 4 5 6 7 >>> b = [[] for _ in range (5 )]>>> b[[], [], [], [], []] >>> b[0 ].extend([1 ,3 ,5 ])>>> b[1 ].extend([2 ,4 ,6 ])>>> b[[1 , 3 , 5 ], [2 , 4 , 6 ], [], [], []]
删除列表元素 列表内元素可重复出现,如何删除列表中的某个元素。
如下方法,遍历每个元素,如果等于删除元素,使用remove删除元素。
1 2 3 4 5 6 7 8 9 10 def del_item (lst, e ): for i in lst: if i == e: lst.remove(i) return lst result = del_item([1 , 3 , 5 , 3 , 2 , 3 ], 3 ) print (result)
执行结果
1 2 3 4 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/demo11.py [1, 5, 2] Process finished with exit code 0
从结果看,这样删除方法是正确的么?例如:我们要删除列表[1, 3, 5, 3, 3, 3, 3, 2, 3]中元素3,结果[1, 5, 3, 2, 3],仍有元素3。
这是为什么呢?遍历lst、remove一次,移掉位置i后的所有元素索引都要减一。所以,一旦删除的元素,重复出现在列表中,就会漏掉一个该删除的元素。
正确做法,找到被删除元素后,进行删除,同时下次遍历索引不加一;若未找到,遍历索引加一,如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 def del_item (lst, e ): i = 0 while i < len (lst): if lst[i] == e: lst.remove(lst[i]) else : i += 1 return lst result = del_item([1 , 3 , 5 , 3 , 3 , 3 , 3 , 2 , 3 ], 3 ) print (result)
执行结果
1 2 3 4 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/demo11.py [1, 5, 2] Process finished with exit code 0
函数默认参数为空 Python函数的参数可设置为默认值。如果一个默认参数类型为list,默认值为设置为[]。
这种默认赋值,会有问题么?
如下的代码:
1 2 3 4 5 6 7 8 9 10 11 12 def delta_val (val, volume=[] ): if volume is None : volume = [] size = len (volume) for i in range (size): volume[i] = i + val return volume rtn = delta_val(10 ) print (rtn)
调用delta_val函数,val值为10,volume默认值,函数返回rtn为空列表。结果如下:
1 2 3 4 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/demo12.py [] Process finished with exit code 0
下面想空列表rtn中,分别添加值1、2,打印rtn,结果符合预期
1 2 3 rtn.append(1 ) rtn.append(2 ) print (rtn)
执行结果:
1 2 3 4 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/demo12.py [1, 2] Process finished with exit code 0
同样方法,再次调用 delta_val 函数,第二个参数还是取默认值。
预期返回值 rtn 应该是空列表,但是结果却出人意料!
1 2 rtn = delta_val(10 ) print (rtn)
执行结果:
1 2 3 4 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/demo12.py [10, 11] Process finished with exit code 0
为什么会返回[10, 11]呢?按照出现的结果,猜测是列表是两个元素从0进行遍历+10后,不正是[10,11],注:不太理解,IDE中Debug下一目了然。
原来调用函数delta_val时,默认参数volume取值为默认值时,并且volume作为函数的返回值。再在函数外面做一些操作,再次按照默认值调用,并返回。整个过程,默认参数volume的id始终未变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def delta_val (val, volume=[] ): print (id (volume)) if volume is None : volume = [] size = len (volume) for i in range (size): volume[i] = i + val return volume rtn = delta_val(10 ) print (rtn)rtn.append(1 ) rtn.append(2 ) print (rtn)rtn = delta_val(10 ) print (rtn)
执行结果
1 2 3 4 5 6 7 8 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/demo12.py 4303077504 [] [1, 2] 4303077504 [10, 11] Process finished with exit code 0
从上面结果来看,2次调用delta_val,volume的内存标识符从未改变。
为了避免这个隐藏的坑,函数的默认参数值切记不能设置为[],而是为None 。这样即便按照默认值调用多次,也会规避此风险。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def delta_val (val, volume=None ): if volume is None : volume = [] size = len (volume) for i in range (size): volume[i] = i + val return volume rtn = delta_val(10 ) print (rtn)rtn.append(1 ) rtn.append(2 ) print (rtn)rtn = delta_val(10 ) print (rtn)
执行结果
1 2 3 4 5 6 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/demo12.py [] [1, 2] [] Process finished with exit code 0
从上面来看,重复调用签名的过程,输出是符合预期的了。
{}和() python中()是一个元组对象,例如:(1.0,3.0)
但是,初始创建的元组对象,若只有一个元素,只用一个对括号是不够的,例如下面single对象不会被解释为元组,而是float。
1 2 3 >>> single = (1.0 )>>> type (single)<class 'float' >
要想被解释为元组,在后面必须要加一个逗号:
1 2 3 >>> single = (1.0 ,)>>> type (single)<class 'tuple' >
为什么要说这个问题呢?是因为在函数调用时,传入参数类型要求为元组。但是在传参时,若不注意丢掉了逗号,就会改变值的类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def fix_points (pts ): for i in range (len (pts)): t = pts[i] if isinstance (t, tuple ): t = t if len (t) == 2 else (t[0 ], 0 , 0 ) pts[i] = t else : raise TypeError('pts的元素类型要求为元组' ) return pts result = fix_points([(1.0 , 3.0 ), (2.0 ), (5.0 , 4.0 )]) print (result)
执行结果
1 2 3 4 5 6 7 8 9 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/demo13.py Traceback (most recent call last): File "/Users/lvjing/PycharmProjects/python_base_project/demo13.py", line 13, in <module> result = fix_points([(1.0, 3.0), (2.0), (5.0, 4.0)]) File "/Users/lvjing/PycharmProjects/python_base_project/demo13.py", line 8, in fix_points raise TypeError('pts的元素类型要求为元组') TypeError: pts的元素类型要求为元组 Process finished with exit code 1
这样传参才是正确的
1 2 3 result = fix_points([(1.0 , 3.0 ), (2.0 ,), (5.0 , 4.0 )]) print (result)
执行结果
1 2 3 4 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/demo13.py [(1.0, 3.0), (2.0, 0, 0), (5.0, 4.0)] Process finished with exit code 0
与之类似的,还有创建集合与字典,它们都用一对{},但是默认返回是字典,而不是集合。
1 2 3 4 5 6 7 8 9 >>> d = {'name' :'xiaohong' , 'age' :'20' }>>> s = {'xiaohong' , '20' }>>> type (d)<class 'dict' > >>> type (s)<class 'set' > >>> d = {}>>> type (d)<class 'dict' >
要想创建空集合,可以使用内置函数set()
1 2 3 >>> s = set ()>>> type (s)<class 'set' >
解包 python中,支持多值赋值给多变量的操作。最常见的用法,一行代码交换两个变量:
1 2 3 4 5 6 7 >>> a, b = 1 , 2 >>> a, b = b, a>>> a2 >>> b1 >>>
但是,面对稍微复杂点的类似操作,如果不搞懂多赋值的执行顺讯,就会掉入陷阱。
如下例子,如果心算出的结果等于a=3,b=5,那么就说明未弄明白执行顺序。
1 2 3 4 5 6 >>> a, b = 1 , 2 >>> a, b = b+1 , a+b>>> a3 >>> b3
从结果来看,不符合我们的预期,是因为,多值赋值是先计算出等号右侧的所有变量值后,再赋值给等号左侧变量。
这种多值赋值,是一种解包(unpack)操作。
既然是解包,那么就要先有打包。等号右侧的多个变量,会被打包(pack)为一个可迭代对象。
赋值操作,就相当于解包。这种解包操作,有时非常有用。比如,foo函数返回一个list,如下:
1 2 3 def foo (): result = [1 , 'xiaoming' , 'address' , 'telephone' , ['' , '' , '....' ]] return result
我们现在需求只要列表的前两项。
更为简洁、紧凑的做法:等号左侧定义两个我们想要的变量,其他不想要的项放到others变量中,并在前加一个*,如下所示:
1 2 3 4 sid, name, *others = foo() print (sid)print (name)print (others)
执行结果
1 2 3 4 5 6 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/demo14.py 1 xiaoming ['address', 'telephone', ['', '', '....']] Process finished with exit code 0
*others会被单独解析为一个list
访问控制 Python是一门动态语言,支持属性的动态添加和删除。而Python面向对象编程(OOP)中,提供很多双划线开头和结尾的函数,它们是系统内置方法,被称为魔法方法。如__getarr__和__setarr__是关于控制属性访问的方法。
重写__getattr__方法,会定义不存在属性时的行为。如下,访问类不存在属性时,程序默认会抛出AttributeError异常。
1 2 3 4 class Student : def __init__ (self, idt, name ): self .id = idt self .name = name
上面的类,构造方法包含id和name两个属性。如果想改变以上默认行为,就可以使用__getattr__。如下,创建Student实例,调用一个不存在的address属性时,给它自动赋值None,需要注意只有某个属性不存在时,__getattr__才会被调用 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Student : def __init__ (self, idt, name ): self .id = idt self .name = name def __getattr__ (self, prop_name ): print ('property %s not existed, would be set to None automatically' % (prop_name,)) self .prop_name = None xiaoming = Student(1 , 'xiaoming' ) print (xiaoming.address)xiaoming.address = 'beijing' print (xiaoming.address)
调用结果
1 2 3 4 5 6 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/demo15.py property address not existed, would be set to None automatically None beijing Process finished with exit code 0
从上面结果来看,直接读取Student中不存在address的属性,就会调用__getattr__,之后,我们又给address赋值,再次调用的时候,就不在调用__getattr__。
还有一个关于属性赋值时行为定义的魔法方法:__setattr__,而它不管属性是否存在,属性赋值前都会调用此函数 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Student : def __init__ (self, idt, name ): self .id = idt self .name = name def __getattr__ (self, prop_name ): print ('property %s not existed, would be set to None automatically' % (prop_name,)) self .prop_name = None def __setattr__ (self, prop_name, val ): print ('%s would be set to %s' % (prop_name, str (val))) xiaoming = Student(1 , 'xiaoming' ) print (xiaoming)
执行结果
1 2 3 4 5 6 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/demo15.py id would be set to 1 name would be set to xiaoming <__main__.Student object at 0x109ff0e20> Process finished with exit code 0
从上面结果来看,只要涉及到属性赋值,赋值前都会调用_setattr__方法。
但是,使用它很容易掉进一个坑,__setattr__里再次设计属性赋值,这样会无线递归下去。
1 2 3 4 def __setattr__ (self, prop_name, val ): print ('%s would be set to %s' % (prop_name, str (val))) self .prop_name = 1.0
执行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/demo15.py id would be set to 1 prop_name would be set to 1.0 prop_name would be set to 1.0 prop_name would be set to 1.0 prop_name would be set to 1.0 prop_name would be set to 1.0 ...... prop_name would be set to 1.0 prop_name would be set to 1.0 Traceback (most recent call last): File "/Users/lvjing/PycharmProjects/python_base_project/demo15.py", line 17, in <module> xiaoming = Student(1, 'xiaoming') File "/Users/lvjing/PycharmProjects/python_base_project/demo15.py", line 3, in __init__ self.id = idt File "/Users/lvjing/PycharmProjects/python_base_project/demo15.py", line 13, in __setattr__ self.prop_name = 1.0 File "/Users/lvjing/PycharmProjects/python_base_project/demo15.py", line 13, in __setattr__ self.prop_name = 1.0 File "/Users/lvjing/PycharmProjects/python_base_project/demo15.py", line 13, in __setattr__ self.prop_name = 1.0 [Previous line repeated 991 more times] File "/Users/lvjing/PycharmProjects/python_base_project/demo15.py", line 11, in __setattr__ print('%s would be set to %s' % (prop_name, str(val))) RecursionError: maximum recursion depth exceeded while calling a Python object Process finished with exit code 1
为保险起见,不要在__setatrr__方法中再做赋值。
中括号访问 对象具有[index],返回某个元素值。那么,它们是怎么实现这种中括号索引的呢?只有重写魔法方法__getitem__,就能实现[index]功能。
如下,类Table是一个最精简的具备中括号索引的类。构造函数__init__传入一个字典,__getitem__返回字典为column_name的字典值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Table (object ): def __init__ (self, df: dict ): self .df = df def __getitem__ (self, column_name ): return self .df[column_name] t = Table({'ids' : list (range (5 )), 'name' : 'Li li Hua hua' .split()}) print (t['ids' ])print (t['name' ])
执行结果
1 2 3 4 5 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/demo16.py [0, 1, 2, 3, 4] ['Li', 'li', 'Hua', 'hua'] Process finished with exit code 0
鸭子类型 Python是动态语言,对函数参数的类型要求很宽松,函数体内使用此类型方法或属性时,只要满足有它们就行,不强制要求必须为这个类或子类。但是,对静态类型语言,如 Java,参数类型就必须为此类型或子类。
例如,下面定义一个Plane类,定义函数using_run
1 2 3 4 5 6 7 8 9 10 11 class Plan (object ): def run (self ): print ('plan is flying...' ) def using_run (duck ): print (duck.run()) using_run(Plan())
执行结果
1 2 3 4 5 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/duck_demo01.py plan is flying... None Process finished with exit code 0
定义一个 Clock 类,它与 Plane 类没有继承关系,但是也有一个 run 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Plan (object ): def run (self ): print ('plan is flying...' ) class Clock (object ): def run (self ): print ('clock is rotating...' ) def using_run (duck ): print (duck.run()) using_run(Clock())
执行结果
1 2 3 4 5 6 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/duck_demo01.py clock is rotating... None Process finished with exit code 0
Plane 对象和 Clock 对象,因都有 run 方法,Python 认为它们看起来就是 duck 类型,因此,Plane 对象和 Clock 对象就被看作 duck 类型。
元类 元类,会被 Pythoner 经常提起,元类确实也有一些使用场合。但是,它又是很高深的、偏底层的抽象类型。Python 界的领袖 Tim Peters 说过:
“元类就是深度的魔法,99% 的用户应该根本不必为此操心。”
只讲一些元类的基本知识,理解下元类是什么,怎么使用元类的一个初步理解。
xiaoming、xiaohong、xiaohua都是学生,这类群体叫做Student
Pyhton定义类的常见方案,使用关键字class:
1 2 3 >>> class Student (object ):... pass ...
xiaoming、xiaohong、xiaohua是类的实例,如下:
1 2 3 >>> xiaoming = Student()>>> xiaohong = Student()>>> xiaohua = Student()
创建后,xiaoming 的__class__属性,返回的便是 Student 类:
1 2 >>> xiaoming.__class__<class '__main__.Student' >
问题在于,Student 类有 class 属性吗?如果有,返回的又是什么?
1 2 >>> xiaoming.__class__.__class__<class 'type' >
返回 type 那么,我们不妨猜测:Student 类的类型就是 type。换句话说,Student 类就是一个对象,它的类型就是 type。因此,类也是对象。
我们都知道,程序猿的世界里都一切皆对象,会有一个更深刻的认识。
Python 中,将描述 Student 类的类被称为:元类 。
既然 Student 类可创建实例,那么 type 类能创建实例吗? 如果能,它创建的实例就叫:类 了。说对了,type 类一定能创建实例,如下所示,type 创建的 Student 类。
1 2 3 >>> Student = type ('Student' , (), {})>>> Student<class '__main__.Student' >
它与使用 class 关键字创建的 Student 类一模一样。
对象序列化 对象序列化,是指将内存中的对象转化为可存储或传输的过程 。很多场景,直接一个类对象,传输不方便。但是,当对象序列化后,就会更加方便,因为约定俗成的,接口间的调用或者发起的 Web 请求,一般使用 JSON 串传输。
实际使用中,一般对类对象序列化。先创建一个 Student 类型,并创建两个实例。
1 2 3 4 5 6 7 8 9 class Student(object): 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.txt 中。注意:json.txt当前目录创建好
1 2 3 4 import json with open('json.txt', 'w') as f: json.dump([ xiaoming, xiaohong] , f, default=lambda obj: obj.__dict__, ensure_ascii=False, indent=2 , sort_keys=True)
生成的文件内容,如下:
1 2 3 4 5 6 7 8 9 10 11 12 [ { "address": "北京", "ids": 1, "name": "xiaoming" }, { "address": "南京", "ids": 2, "name": "xiaohong" } ]
日志 在调试代码,我们往往习惯使用 print 函数。通过 print,一些异常信息、变量值信息就会显示在控制台中,然后帮助我们锁定 Bug,找出问题。
但是,当项目上线后,程序一般运行在 Linux 服务器上。如果程序出现异常行为,要想通过 print 函数找出问题,可能还得安装调试代码的 IDE,在服务器上做这些事情,可能不太方便。
一般的解决方案,在代码中想 print 的信息,也要写入到日志文件中,在磁盘上保存起来。此时,遇到问题后,找到并分析对应的日志文件就行,这种解决问题的方法更可取,效率也会更高。
日志写入不是我们想象的这般简单。如果一直向同一个文件里写,文件就会变得很大很大;也不方便分析。更糟糕的是,文件越来越大,当大小等于磁盘容量时,后面的日志信息就无法再写入。当然,还有更多问题会出现。
所以,别小看写日志,我们得需要设计一套行之有效的管理体系,对日志实施有效的管理。
像大名鼎鼎的、适用于 Java 开发的 log4j,便是一套设计优秀的日志管理包。
Python 中,也有一个模块 logging,也能做到高效的日志管理。
例如,logging 模块,能按照指定周期切分日志文件。这一切的规则,都是为了实现对日志的高效管理。这些需求背后,对应着一套解决方案,也就是 logging 库,和它的四大组件:记录器 、处理器 、过滤器 和格式化器 。
下面是一个基本的日之类,同时将日志显示在控制台和写入文件中,同时按照天为周期切分日志文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import loggingfrom logging import handlersclass Logger (object ): kv = { 'debug' : logging.DEBUG, 'info' : logging.INFO, 'warning' : logging.WARNING, 'error' : logging.ERROR, 'crit' : logging.CRITICAL } def __init__ (self, filename, level='info' , when='D' , backCount=3 , fmt='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s' ): self .logger = logging.getLogger(filename) format_str = logging.Formatter(fmt) self .logger.setLevel(self .kv.get(level)) sh = logging.StreamHandler() sh.setFormatter(format_str) th = handlers.TimedRotatingFileHandler( filename=filename, when=when, backupCount=backCount, encoding='utf-8' ) th.setFormatter(format_str) self .logger.addHandler(sh) self .logger.addHandler(th)
创建 log 对象,日志级别为 debug 及以上的写入日志文件:
1 log = Logger('all.log' , level='debug' ).logger
创建 Student 类,score 属性取值只能为整型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 from logging_demo import Loggerlog = Logger('all.log' , level='debug' ).logger class Student : def __init__ (self, id , name ): self .id = id self .name = name log.info('学生 id: %s, name: %s' % (str (id ), str (name))) @property def score (self ): return self .__score @score.setter def score (self, score ): if isinstance (score, int ): self .__score = score log.info('%s得分:%d' % (self .name, self .score)) else : log.error('学生分数类型为 %s,不是整型' % (str (type (score)))) raise TypeError('学生分数类型为 %s,不是整型' % (str (type (score)))) xiaoming = Student(10010 , 'xiaoming' ) xiaoming.score = 88 xiaohong = Student(10010 , 'xiaohong' ) xiaohong.score = 90.6
当 score 被赋值为 90.6 时,会抛出异常,并写入错误日志到文件 all.log 中,供我们日后分析使用。
执行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 /Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/Student.py 2023-01-30 17:23:03,074 - /Users/lvjing/PycharmProjects/python_base_project/Student.py[line:10] - INFO: 学生 id: 10010, name: xiaoming 2023-01-30 17:23:03,074 - /Users/lvjing/PycharmProjects/python_base_project/Student.py[line:20] - INFO: xiaoming得分:88 2023-01-30 17:23:03,074 - /Users/lvjing/PycharmProjects/python_base_project/Student.py[line:10] - INFO: 学生 id: 10010, name: xiaohong 2023-01-30 17:23:03,074 - /Users/lvjing/PycharmProjects/python_base_project/Student.py[line:22] - ERROR: 学生分数类型为 <class 'float'>,不是整型 Traceback (most recent call last): File "/Users/lvjing/PycharmProjects/python_base_project/Student.py", line 30, in <module> xiaohong.score = 90.6 File "/Users/lvjing/PycharmProjects/python_base_project/Student.py", line 23, in score raise TypeError('学生分数类型为 %s,不是整型' % (str(type(score)))) TypeError: 学生分数类型为 <class 'float'>,不是整型 Process finished with exit code 1
查看all.log文件
1 2 3 4 5 2023-01-30 17:23:03,074 - /Users/lvjing/PycharmProjects/python_base_project/Student.py[line:10] - INFO: 学生 id: 10010, name: xiaoming 2023-01-30 17:23:03,074 - /Users/lvjing/PycharmProjects/python_base_project/Student.py[line:20] - INFO: xiaoming得分:88 2023-01-30 17:23:03,074 - /Users/lvjing/PycharmProjects/python_base_project/Student.py[line:10] - INFO: 学生 id: 10010, name: xiaohong 2023-01-30 17:23:03,074 - /Users/lvjing/PycharmProjects/python_base_project/Student.py[line:22] - ERROR: 学生分数类型为 <class 'float'>,不是整型