Skip to content

基础

(一)常见报错排查

  1. 命令行窗口执行python程序,遇到 python d:\>python my.py 'python' 不是内部或外部命令,也不是可运行的程序或批处理文件。 在Path环境变量中添加python解释器目录;或全路径引用python解释器
  2. 命令行窗口执行python程序,遇到 python d:\>python my.py python: can't open file 'my.py': [Errno 2] No such file or directory 要执行的python代码文件不在目录上->进入代码所在目录或输入代码文件的全路径
  3. 命令行窗口执行python程序,遇到 ```python d:>python Python 3.6.2 (v3.6.2:5fd33b5, Jul 8 2017, 04:14:34) [MSC v.1900 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information.

    python my.py File "", line 1 python my.py ^ SyntaxError: invalid syntax

    ```
    python解释器的交互式命令行中无法执行命令,只能执行python代码
    
  4. 在python交互式命令行,执行一条Python语句,遇到 ```python

    print('hello') File "", line 1 print('hello') ^ IndentationError: unexpected indent

    ```
    未知缩进,python语句一定要顶到最前面
    
  5. 在python交互式命令行,执行一条Python语句,遇到 ```python

    Print('hello') Traceback (most recent call last): File "", line 1, in NameError: name 'Print' is not defined

    ```
    Print未定义
    

(二)基础

1. 基础

  • 字面量:直接将对象写出来
  • 转义符(Python字符串中 转义字符都是用反斜杠 \ 开头): \n 表示换行字符、\t 表示tab字符、 \x41 表示 ASCII编码为 0x41 的字符(也就是字符 A,后面字符编码会学习到 )、\u767d 表示 unicode编码为 0x767d 的字符(也就是汉字 白,后面字符编码会学习到 )
  • 如果字符串中本身需要有反斜杠,并非转义,比如Windows下面的路径:(1)在反斜杠前面再加一个反斜杠;(2)在字符前面加上一个 r ,表示字符串内容是raw string,无需转义
  • 环境变量环境变量介绍
  • 哈希值计算:理解为一种算法,可以根据一个原始数据,计算出一个结果数据,而且不同的原始数据,哪怕只有一丁点的不同,计算出的结果数据都不同。
  • 显示换行\:当一行代码过长时,可以使用反斜杠(\)显式换行
  • weget下载:-P参数指定下载目录;-O参数指定

2. 初学函数

  • 参数:提供 函数完成功能 所必须的信息
  • return关键字:函数返回的结果
  • 函数的参数定义:一旦有了缺省值/指定参数名,后面所有的参数都必须有缺省值/指定参数名
  • 变量有效范围:局部变量:函数内部的变量;全局变量:所有函数外部的变量
  • 范围覆盖:函数内使用局部变量(全局与局部变量同名);通过 global + 函数名 先声明(在函数内对全局变量进行操作)

3. 判断

  • 如果not、 and 和 or 一起使用, 注意 是先计算 not , 再计算 and 部分, 最后再计算 or
  • 用if not var : 来判断变量是否为空:当变量var 作为数字 是 0 ,或者作为字符串、列表、元组、字典 为 空 , 或者为 None ,效果就类似于 False。那么结果这个 if not var就等同于 if not False,也就是等同于 if True,判断条件就会成立,反之 if var就等同于if False,判断条件自然就不成立。

4. 对象的方法——对象所拥有的函数

字符串(不可变,内容本身不因为调用方法而改变,通常采用重新赋值) - count(计数):返回字符串对象包含了多少个参数指定的字符串 - find:在字符串中查找 参数 子字符串,并返回该 参数字符串 在其中 第一个 出现的位置索引,如果没有则返回-1;第二个参数,用来指定查找字符串范围,从参数的位置开始往后查找。 - split(截取):以参数字符串为分割符 ,将字符串切割为多个字符串,作为元素存入一个列表,并返回这个列表(不指定分隔符时,会自动处理空白字符);splitlines——将字符串按换行符切割(常用在文件处理中,读出文件内容存入字符串,然后调用字符串的 splitlines方法,把每行内容依次存入列表。) - join(连接):将列表中的字符串元素 以某字符串为连接符,连接 为一个字符串 - strip、lstrip、rstrip:分别为将字符串前后、前、后的空格删除 - replace:替换字符串里面所有指定的子字符串为另一个字符串 - startwith、endwith:检查字符串是否以参数指定的字符串开头、结尾 - isdigit:检查字符串是否全由数字构成,是返回True,否返回False - 切片操作 [::-1]::: 表示切片字符串的从头到尾,也就是全部内容, 而步长 为-1表示,颠倒过来取元素。 - .title():每个单词的首字母变大写,其余字母变小写。 列表 - append:改变列表的内容,在后面添加一个元素。append方法的返回值是None,而不是新的列表对象。 - extend:在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)。 - insert:在指定位置插入一个元素,insert方法的返回值也是 None - pop(取出并删除索引):参数是要取出的元素的索引,返回值是提取出的元素 - remove(删除):删除元素的值——从第1个元素开始,寻找和参数对象相等的元素,如果找到了,就删除。找到后,不会继续往后寻找其它相等的元素。返回值也是None - reverse:将列表元素倒过来 - index:返回参数对象在列表中的位置(索引) - sort;对列表中进行排序,通常是数字和字符串

5. 字符串格式化

printf风格 - print('税前薪资:%s元,缴税:%s元,税后薪资:%s元' %(salary,tax,aftertax)) - %s是占位符,表示待会有数据要填入到这里(%s调用str()) - 至少10字符——%10s、改变为左边对齐——%-10s - 整数(%d,制定宽度时希望不足的部分补零——%010d) - 浮点数(%f,保留小数点后两位数字——%010.2f) f-string格式化(python解释器版本为3.6以后的版本) - print(f'税前薪资是:{salary}元, 缴税:{tax}元, 税后薪资是:{aftertax}元') - f'{eg:10}'——制定宽度为10字符,不足则在前面加上空格,输出整体显得很清爽 - f'{eg:<10}'、f'{eg:>10}'——左右对齐,数字对象缺省为右对齐,字符串对象缺省为左对齐 - f'{eg:10.1f}'——指定小数点后保留1位 - f'{eg:010}'——不足补零,补空格改为补零,这是对数字的补零。如果要对字符串不足补零,就应该 使用符号 < 或者 > 同时指定左右对齐方式。 - 16进制格式化:f'{65535:x}'(小写)、f'{65535:X}'(大写) - 字符串内容中有花括号:采用双写进行转义print(f'文章中 {{ 符号 出现了 {times} 次')

6. 循环(while循环和for循环:让计算机反复的做一些事)

while循环:检查一个条件表达式,只要条件表达式计算结果为True 时, 就执行下面缩进的代码。 for循环:通常是从一个sequence类型,比如 字符串、列表 或者 元组中 依次取出每个元素进行操作。 - range()可以放入3个参数,依次为起点、终点、步长 - enumerate()每次迭代返回一个两元素的元组,依次为元素索引和元素本身 - break终止循环:解释器执行到 循环内的 break 语句,就会从循环while 退出 break和return的区别: - return 只能用在函数里面,因为 return 是表示从函数中返回; - 函数中的循环体内的代码, 使用 return和 break都可以从循环中跳出:break只是跳出循环,如果循环后面还有代码,会进行执行;return则会从函数里面立即返回,函数体内的后续任何代码都不执行了。 - continue只是当前这次循环结束,就是这次循环 continue 后面的代码不执行了, 后续的循环还要继续进行。
- 内置函数 zip:依次组合多个列表或者元组,将里面相同索引的数据放入一组,方便循环使用(如果,两个列表里面元素个数不相等, zip以少的那个为准)
- 列表推导式:将一个列表里面的每个元素经过简单的处理生成另一个列表的操作。 python list1 = [1,2,3,4,5,6] list2 = [num**3 for num in list1] list3 = [i for i in range(1, 10)] 循环嵌套:循环里面还有循环 - 循环嵌套里的break:只能终止内层循环 - 循环嵌套里的continue:只对内层循环有效 不要在遍历列表过程中删除索引

7. 字符编码

字符串在 内存中的表示时,通常使用UTF-16或其变种来表示字符串,这方便进行字符的快速索引和操作。 字符串在 存储/传输时的表示时,更倾向于使用UTF-8编码,主要原因是空间效率和兼容性 - 对英文为主的内容: - UTF-8编码一个英文字符(ASCII字符)只占用1个字节 - UTF-16编码一个英文字符(ASCII字符)至少占用2个字节 - 对中文为主的内容: - UTF-8编码一个常用汉字占用3个字节 - UTF-16编码一个常用汉字占用2个字节 - UTF-8编码完全向后兼容ASCII,一个只认识ASCII的旧系统也能安全地处理UTF-8编码的文本(它会忽略多字节字符,但不会破坏数据流) Python语言要对字符串对象进行存储和传输的时候,通常要使用字符串的 encode 方法,参数指定为 'utf8',将其编码为一个 bytes 对象。 - bytes对象的底层就是用一个个的字节来存储字符串中的文字的。 - 同样的字符串,用不同的编码方式,有时会产生不同的bytes结果。 python print ('你好'.encode('utf8')) # 输出 b'\xe4\xbd\xa0\xe5\xa5\xbd' print ('你好'.encode('gbk')) # 输出 b'\xc4\xe3\xba\xc3' - 开头b表示是一个字符串bytes对象,\x说明用16进制表示一个字节 - encode方法返回的是编码后的字节串对象bytes - 编码为字节串对象 bytes 就可存储到文件或者传输到网络中去了 字符串解码:要解码字节串,必须要知道这个字节串是用什么字符编码的方式进行编码的。如果知道了,就可以用字节串对象的decode方法 进行解码,参数指定了编码方式 一些字符编解码技巧 - unicode数字转换为字符:使用函数chr(20013) - 字符转换为unicode数字:使用函数ord('中') - 字符串编码为unicode转义数字:unicode-escape ,就是直接用unicode数字字符串表示字符,'白月黑羽'.encode('unicode-escape')->b'\\u767d\\u6708\\u9ed1\\u7fbd' - 用unicode转义数字写字符串:'\u767d\u6708\u9ed1\u7fbd'->'白月黑羽' - 直接用16进制数字创建bytes:b'\xb0\xd7\xd4\xc2\xba\xda\xd3\xf0'.decode('gbk')->'白月黑羽' - 字符串和16进制表示字节的字符串:

>>> a = b'hello,123' 
>>> a.hex() 
'68656c6c6f2c313233'
# 反向操作:将16进制表示字节的字符串 转化为 字节串
>>> bytes.fromhex('68656c6c6f2c313233') 
b'hello,123'

8. 文件读写

文本模式:通常,对文本文件,都是以文本模式打开。文本模式打开文件后,我们的程序读取到的内容都是字符串对象,写入文件时传入的也是字符串对象。 open函数的参数

open(
    file, 
    mode='r', 
    buffering=-1, 
    encoding=None, 
    errors=None, 
    newline=None, 
    closefd=True, 
    opener=None
    )
  • file:file参数指定要打开文件的路径
  • mode:指定的文件打开的模式(缺省时为r)
    • r:只读文本模式打开
    • w:只写文本模式打开(创建新文件写入内容,或清空某个文件重新写入内容)
    • a:追加文本模式打开(从某个文件末尾添加内容)
    • encoding:指定读写文本文件时,使用的字符串编解码方式 调用open函数时,如果传入了encoding参数值:
    • 后面调用write写入字符串到文件中,open函数使用指定encoding编码为字符串
    • 后面调用read从文件中读取内容,open函数使用指定encoding解码为字符串对象
    • read()方法会在读取文件中的原始字节串后, 根据上面指定的encoding解码为字符串对象返回
    • read函数有参数size,读取文本文件的时候,用来指定这次读取多少个字符。 如果不传入该参数,就是读取文件中所有的内容。
    • readlines()方法:该方法会返回一个列表。 列表中的每个元素依次对应文本文件中每行内容。但是这种方法,列表的每个元素对应的字符串 最后有一个换行符。 如果你不想要换行符,可以使用字符串对象的splitlines方法
f = open('tmp.txt')
content = f.read()   # 读取全部文件内容
f.close()  

# 将文件内容字符串 按换行符 切割 到列表中,每个元素依次对应一行
linelist = content.splitlines()
for line in linelist:
    print(line)

二进制(字节)模式 - 二进制读:open函数的mode参数'rb' ,读取二进制字符串到bytes对象 - 二进制写:open函数的mode参数'wb',只能传递bytes对象到write方法 - decode() 方法以 encoding 指定的编码格式解码字符串。默认编码为字符串编码。 - 如果想在代码中直接用数字表示字符串内容,并写入文件

content = b'\xe7\x99\xbd\xe6\x9c\x88\xe9\xbb\x91\xe7\xbe\xbd'
f.write(content)    
  • 任何文件都可以通过字节方式进行读取和写入,下面是以字节方式实现文件拷贝
def fileCopy(srcPath,destPath):
    srcF = open(srcPath,'rb')
    content = srcF.read()
    srcF.close()

    destF = open(destPath,'wb')
    destF.write(content)
    destF.close()

fileCopy('1.png','1copy.png')

with语句 打开文件,就不需要close方法来关闭文件(可以避免忘记)

# open返回的对象 赋值为 变量 f
with open('tmp.txt') as f:
    linelist = f.readlines() 
    for line in linelist:
        print(line)

写入缓冲:执行write方法写入字节到文件中的时候,其实只是把这个请求提交给 操作系统。操作系统为了提高效率,通常并不会立即把内容写到存储文件中,而是写入内存的一个 缓冲区。等缓冲区的内容堆满之后,或者程序调用close 关闭文件对象的时候,再写入到文件中。 如果真的希望,在调用write之后,立即把内容写到文件里面,可以使用 文件对象的 flush方法。

f = open('tmp.txt','w',encoding='utf8')

f.write('白月黑羽:祝大家好运气')
# 立即把内容写到文件里面
f.flush()

# 等待 30秒,再close文件
import time
time.sleep(30)

f.close()

python文件的字符编码默认为UTF-8,如果代码文件必须是其他类型编码,比如 GBK 编码,就必须在代码文件中声明该文件的编码方式, 让解释器知道。 声明的方式,是在代码文件的第1行或者第2行,加上声明注释,如coding=gbk

9. 模块和库

在Python中,一个代码文件(也就是一个.py文件),我们也叫它一个模块(Module)。a.py 文件,我们就可以称之为模块a,b.py 文件,我们就可以称之为模块b。 (1)模块之间的调用 - 通过import关键字导入模块 import save->save.savetofile(x, y) - 通过 from import关键字导入其它模块里面的标识符(包括变量名和函数名等)from save import savetofile->savetofile(x, y) - 一些技巧:导入多个模块(import aa, bb, cc);导入一个模块的很多标识符(from aa import func1,var1,func2,var2),导入一个模块的所有可导入标识符(from aa import *);给模块起别名(from save import savetofile as savetofile)。 - 将模块放入包中:放模块文件的目录 __init__.py(python3.3以前需要。以后不需要) 可以作为包的初始化文件,里面放入一些初始化代码,有独特的作用。 下面是一个商城产品目录结构的例子

    stock/                        ---   顶层包
        __init__.py           ---   stock包的初始化文件
        food/                 ---   food子包
                __init__.py
                pork.py
                beef.py
                lobster.py
                ...
        furniture/            ---   furniture子包
                __init__.py
                bed.py
                desk.py
                chair.py
                ...
        kitchen/              ---   kitchen子包
                __init__.py
                knife.py
                pot.py
                bowl.py
                ...

(2)库的概念 - python标准库:内置类型和内置函数无须使用import导入,可以直接使用;还有些标准库,需要使用import导入,才能使用。常见有 sys, os, time, datetime, json,random 等。 - 要结束Python程序,sys库里面的exit函数:sys.exit(0) - 获取字符串形式的当前日期和时间,可以使用datetime库:

import datetime

# 返回这样的格式 '20160423'
datetime.date.today().strftime("%Y%m%d") 

# 返回这样的格式 '20160423 19:37:36'
datetime.datetime.now().strftime("%Y%m%d %H:%M:%S")
  • 获取随机数字,可以使用random库:from random import randint (3)解释器寻找模块
  • 首先在解释器内置模块( builtin modules )中寻找 是否有 xxx;
  • 依次到 sys.path 包含的目录下去寻找 有没有同名的模块(解释器启动的时候,是从根据下面这些规则 添加路径到 sys.path 列表里的) :
    • 启动脚本文件所在的目录
      • PYTHONPATH环境变量里的目录
      • Python解释器的 缺省安装目录(installation-dependent default) 添加一些目录,作为模块搜索路径
      • 在windows下面,可以用这样的命令添加(只在当前命令行窗口有效) set PYTHONPATH=c:\myproject;c:\myproject\mylib (4)安装第三方库 pip install packageName 列出已安装的第三方库:pip list 查看某个已安装的模块信息:pip show xxx pip升级库版本到最新:使用命令参数-U pip指定某个库特定版本:使用==后面加上版本号 pip卸载某个库:pip uninstall packageName

        [!warning] 如果在公司使用代理上网 pip安装可能出现SSL证书校验错误,可以加上信任参数 --trusted-host ,如下所示 pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn

10. IDE配置

(1)代码助手 - 查看详情:ctrl + 左键;返回:go -> back ;alt + 左右箭头 - 找哪里使用的某函数:选中,右键,转到引用go to reference - 变量、函数重命名:选中,F2;搜索(大小写敏感、整词匹配),点选批量替换 (2)调试参数配置 python调试 - request:attach(在已经运行的程序上去debug) - justMyCode:仅调试自己的代码(False则包括库的代码都要调试) - args:指定 库调试 - 新建一个配置->python调试->module debug->美化

11. 字典(提供了一种特别的结构, 就是存放 键值对 数据。这种结构使得字典,特别像一张表)

(1)定义用花括号{},每个元素间用逗号,隔开,每个元素都是一个键值对,键和值直接用冒号:隔开。 (2)字典的内容可以改变:

members = {}  #定义一个字典
members['account1'] = 13
members['account2'] = 12
members['account2'] = 88  #修改一个元素key对应的值
val = members.pop('account1')  #删除一个元素,返回key对应的值
del members['account1']  #使用del关键字删除一个元素
  • 检查字典是否存在某个key:in 和 not in
if '小张' in dict1.keys(): 
    print('存在')
# `if '小张' in dict1.keys()` 是会取出字典中所有的key 放到一个类似列表的对象中, 然后依次比较每个元素是否等于小张。这样效率很低。
if '小张' in dict1:
    print('存在')
# 字典对象判断一个key 是否在字典里面,会有高效算法,不会依次遍历,性能会高很多。
  • 遍历字典(字典对象的items方法) items方法,返回的是一个类似列表一样的对象,其中每个元素就是 键值组成的元组
members = {
    'account1'  : 13 ,
    'account2'  : 12 ,
    'account3'  : 15 ,
}

for account,level in members.items():
    print (f'account:{account}, level:{level}')
  • keys方法,返回的是将字典所有的键存入的 一个类似列表的对象
  • values方法,返回的是将字典所有的值存入的 一个类似列表的对象
  • clear方法可以清空字典对象里面的所有元素
    • 重新赋值:members = {}
    • 区别就在于clear方法,members变量指向的还是原来的字典对象。而重新赋值的方法,members变量指向的就是一个新的空字典对象。原来的字典对象呢?因为没有被引用,就会被Python解释器在合适的时候 从内存中清除掉。
    • update方法,字典里面加上另外一个字典的内容
    • len(),得到字典元素的个数
  • 用xx去查xx的话,就是使用字典:字典根据key来查询是有算法优化的,比循环一个个找的效率高非常多

12.类:定义一个类型就是描述这些类型的实例的 公共特征

(1)为了处理系统里面的对象和现实世界的对象存在对应关系,自己定义一个类型

class BenzCar:    
    brand   = '奔驰'  # 品牌属性
    country = '德国'  # 产地属性

    @staticmethod
    def pressHorn(): 
        print('嘟嘟~~~~~~')
  • class后面加类名称,
  • brand、country都是类的属性,要想得到属性值可以用类名.属性名的方法
  • pressHorn是类型的一个方法,@staticmethod说明是一个静态方法,要想执行该类的静态方法,像BenzCar.pressHorn()就可以了
  • 类属性:该类的每个实例的都一样的共同特征 (2)类与实例:具体的对象是该类型的实例 3是int对象的实例,'abc'是str类型的实例,[1, 2, 3]是列表对象的实例; 类属性是类的共同特征属性; 每个实例独有的属性,称之为类的实例属性,实例属性通常是在类的 初始化方法 __init__ 里面定义的。 下面的初始化方法 __init__ ,就创建了两个实例属性color和engineSN。
class BenzCar:    
    brand   = '奔驰'  
    country = '德国'  

    @staticmethod
    def pressHorn(): 
        print('嘟嘟~~~~~~')

    # 初始化方法, 注意前后各有两个下划线
    def __init__(self):
        self.color  =  'red'        # 颜色
        self.engineSN = '837873398' # 发动机编号
  • 为什么__init__方法叫初始化方法
    • 首先,解释器在内存中创建一个该类的实例对象;
    • 然后,解释器会查看这个类是否有__init__方法,如果有,就会去调用它。 __init__ 是 创建好实例后 立即就要 执行 的方法,所以称之为初始化方法。 通常会在__init__方法里面执行一些初始化的动作,主要就是创建该实例的实例属性。 __init__ 方法的第一个参数是 self:解释器执行实例化代码,会先在内存中创建该类实例对象,然后调用类的__init__方法。调用__init__方法时,就将实例对象传递给self参数。self 参数变量 指向的 就是实例对象本身。
self.color  =  'red'         # 颜色
self.engineSN = '8378738398' # 发动机编号
  • 类方法:行为不需要访问到类的具体的实例属性
  • 类的静态方法要在方法定义 上面加上 @staticmethod 的修饰,而类的实例方法不需要任何修饰。
  • 通常类的实例方法,都是要访问类的实例属性的。包括:创建、修改、删除类的实例属性。因为实例方法就是要操作实例独有的属性,否则不操作任何实例属性的话,就应该定义为类方法。比如__init__初始化方法,就是一个实例方法,它通常要创建一些实例属性。而pressHorn方法是类的静态方法,静态方法是不能访问实例属性的。
  • 虽然定义时,__init__方法有n个参数,但在调用类型实例化时只需要传入n-1个参数,因为self参数需要传入实例对象本身,解释器会帮我们自动传入。
  • 注意: 如果实例属性名称和静态属性重复了,通过类实例访问该属性,访问的是实例属性。通过类名访问该属性,访问的是类属性。
class Car:
    brand = '奔驰'
    name = 'Car'

    def __init__(self):
        # 可以通过实例访问到类属性
        print(self.brand)

        # 定义实例属性和类属性重名
        self.name = 'benz car'

c1 = Car()

print(f'通过实例名访问name:{c1.name}')
print(f'通过类名  访问name:{Car.name}')

(3)类之间的关系 - 继承关系:类型直接存在范围包含关系 - 通常将被继承的类成为父类或基类,继承类称为子类或派生类 - 定义子类时必须指定它的父类是什么:在类名的后面的括号里写上父类的名字 - 子类会自动拥有父类的一切属性和方法 - 一个子类在继承父类的一切特性的基础上,可以有自己的属性和方法,比如: ```python class Benz2018(BenzCar): price = 880000 model = 'Benz2018'

def __init__(self,color,engineSN,weight):
    # 先调用父类的初始化方法
    BenzCar.__init__(self,color,engineSN)
    self.weight = weight # 车的重量
    self.oilweight = 0  # 油的重量

# 加油
def fillOil(self, oilAdded):
    self.oilweight +=  oilAdded 
    self.weight    +=  oilAdded
```
- 子类`Benz2018`,新增了两个类属性,新增了两个实例属性,新增了一个实例方法`fillOil`,`filloil`会导致实例属性`weight`和`oilweight`变化,所以必须是实例方法。
- 子类的初始化方法里面,如果有一部分的初始化代码和父类的初始化相同(通常都是这样),需要*显式的*调用父类的初始化方法`__init__`.
    - 如果子类没有自己的初始化方法,实例化子类对象时,解释器会自动调用父类初始化方法;
    - 如果子类有自己的初始化方法,实例化子类对象时,解释器就不会自动化调用父类的初始化方法;
    - 调用父类的方法,除了直接用父类的名字`BenzCar`,还可以使用 函数 `super()`。
        - 使用 `super` 的好处之一就是子类中调用父类的方法,不需要 显式指定父类的名字。 代码的可维护性更好;(想象一下,如果`BenzCar`有很多子类,如果哪一天`BenzCar`类改了名字,采用`super`这样的写法,就不需要修改子类的代码了。)
        - 另一个更重要的好处是在 **多重继承** 的情况,super能有效的保证继承链上的方法被正确调用到;
        - 注意 super不仅仅可以调用父类的初始化方法,也可以调用父类的其他方法。
  • 组合关系:一个类实例对象的属性里面包含另外一个类实例对象
    • 下面例子里,奔驰汽车对象就包含了4个轮胎Tire对象
    • 对象的属性变量保存了组合它的那些对象
# 轮胎
class Tire:    
    def __init__(self,size,createDate):
        self.size  =  size  # 尺寸
        self.createDate = createDate # 出厂日期

class BenzCar:    
    brand   = '奔驰'  
    country = '德国'  

    def __init__(self,color,engineSN,tires):
        self.color  =  color  # 颜色
        self.engineSN = engineSN # 发动机编号
        self.tires   =  tires

# 创建4个轮胎实例对象
tires = [Tire(20,'20160808')  for i in range(4)]
car = BenzCar('red','234342342342566',tires)

# 通过BenzCar对象访问其内部的轮胎对象
print(car.tires[0].size)

# Python解释器对这个表达式 `car.tires[0].size` 是从左到右依次执行的,如下所示
car.tires   # BenzCar实例的tires属性,得到一个列表,里面是四个 Tire 实例对象
car.tires[0]   # 得到BenzCar实例的tires属性列表里面的第1个Tire 实例对象
car.tires[0].size  # 得到BenzCar实例的tires属性列表里面的第1个Tire 实例对象的size属性

(4)类方法 - 使用@classmethod的修饰,说明这是该类的一个类方法

class BenzCar:    
    brand   = '奔驰'  
    country = '德国'  

    @classmethod
    def pressHorn(cls): 
        print(f'当前的类是 {cls}')
        print(f'本车品牌是 {cls.brand},来自 {cls.country}')
        print('嘟嘟~~~~~~')

BenzCar.pressHorn()

运行结果如下: 当前的类是 <class '__main__.BenzCar'> 本车品牌是 奔驰,来自 德国 嘟嘟~~~~~~ - 类方法和静态方法相比,唯一的不同是调用时解释器会自动传入类对象作为类方法的第一个参数 - 类方法一个常见的使用场景是父类的非实例方法需要访问子类的类属性

class Car:    
    brand   = '未知,在子类中定义'  
    country = '未知,在子类中定义'  

    @classmethod
    def pressHorn(cls): 
        print(f'当前的类是 {cls}')
        print(f'本车品牌是 {cls.brand},来自 {cls.country}')
        print('嘟嘟~~~~~~')

class BenzCar(Car):    
    brand   = '奔驰'  
    country = '德国'  

class CamaryCar(Car):    
    brand   = '凯美瑞'  
    country = '日本' 

BenzCar.pressHorn()
CamaryCar.pressHorn()

(5)打印实例对象,必须定义 __repr__ 方法,如下

class BenzCar:
    brand   = '奔驰'

    def __init__(self,color='black'):
        self.color = color

    def __repr__(self):
        return f"BenzCar object: brand = 奔驰, color = {self.color}"

# 打印实例对象
print(BenzCar())
  • 因为 print 一个对象时,其本质就是打印 该对象的 __repr__ 方法的返回值

13.异常

(1)捕获和处理异常:try...except...

while True:
    try:
        miles = input('请输入英里数:')
        km = int(miles) * 1.609344
        print(f'等于{km}公里')
    except ValueError:
        print('你输入了非数字字符')
  • 执行保护区中代码时,出现异常,解释器会结束保护区中后续代码的执行,并检查这个异常的类型是否匹配后面的except 语句中声明的类型。如果匹配上,解释器知道程序对此种异常是预料到的,并且有对应的处理方案,也就是匹配的except下面缩进的代码。解释器就执行匹配的except下面缩进的代码,不会因此中止程序。
  • 如果我们开发程序的时候,估计某个代码段中可能出现好几种类型的异常,可以使用多个except 代码段,分别捕获多种类型的异常 (2)获取异常对象:使用except 语句匹配异常类型的时候, 可以使用as关键字,后面加一个变量名
try:
    100/0
except ZeroDivisionError as e:
    print (f'异常对象信息:{e}')

(3)匹配所有异常:在写一段代码的时候,不知道这段代码会抛出什么样的异常,并且我们不希望程序因为异常而中止。这时我们可以匹配所有类型的异常,这样任何类型的异常发生都不会终止程序了。几乎所有 的异常都是 Exception 的子类(除了 SystemExit 和 KeyboardInterrupt 等 少数几个)。 所以 Exception能匹配 几乎所有 类型的异常。

# Exception
try:
    100/0
except Exception as e:
    print('未知异常:', e)

# 简洁写法
try:
    100/0
except:
    print('未知异常:')

# 想了解异常的详细信息,使用traceback模块
import traceback

try:
    100/0
except :
    # format_exc函数可以显示异常的信息 和 异常产生处的函数调用栈的信息
    print(traceback.format_exc())

(4)自定义异常 定义异常,比如我们写一个用户注册的函数, 要求用户输入的电话号码只能是中国的电话号码,并且电话号码中不能有非数字字符。

# 异常对象,代表电话号码有非法字符
class InvalidCharError(Exception):
    pass

# 异常对象,代表电话号码非中国号码
class NotChinaTelError(Exception):
    pass

# 出现相应错误的时候,我们就可以使用raise关键字来抛出对应的自定义异常
def  register():
    tel = input('请注册您的电话号码:')

    # 如果有非数字字符
    if not tel.isdigit(): 
        raise InvalidCharError()

    # 如果不是以86开头,则不是中国号码
    if not tel.startswith('86'): 
        raise NotChinaTelError()

    return tel

try:
    ret = register()
except InvalidCharError:
    print('电话号码中有错误的字符')
except NotChinaTelError:
    print('非中国手机号码')

14.再学函数

(1)可变参数——前面加一个星号的参数,称之为可变参数 - 在调用该函数的时候,Python解释器会创建一个 tuple 赋值给这个参数变量。并且会把 传入的数据对象 放到这个tuple对象里面。 - 参数展开如下:

onebatch = ['张飞', '典韦', '关羽']
# 参数展开
printAge (*onebatch)
  • 注意:在函数定义的时候前面加上星号是可变参数;在调用函数的时候,传入参数前面加上星号,是参数展开。 (2)关键字可变参数——前面加2个星号的参数
  • 在调用该函数的时候,Python解释器会创建一个 dict (字典) 赋值给这个参数变量,并且会把传入的数据对象 分别放到这个dict对象里面;
  • 传入的参数对象,必须是像name=value这种带参数名和参数值的,放到dict对象时,参数名是字典元素的key,参数值是字典元素的value。
  • 参数展开如下:
onebatch = {'李白': 20, '杜甫': 24}
# 参数展开
addStudents(table1, **onebatch)

15.通过搜索解决问题

(1)搜索网站:谷歌>bing>>>>>百度 - 国外网站:英文阅读能力 (2)关键词: 比如:你使用Python和openpyxl库修改excel的时候,碰到这样的问题 如果原来的文件中有图片,使用openpyxl库 修改并保存后,这些图片没有了!! 这种情况下,有用的关键词有:Python、openpyxl、修改 excel、图片丢失