Python简易教程(MUD中可调用Python扩展功能)

在MUD中调用Python的方式:https://bbs.mud.ren/threads/244

重要提示

  • 一定推荐安装Anaconda,使用 Jupyter Notebook 来学习,用markdown做笔记并随时插代码运行,非常好用。
  • python是区分大小写的,不管是变量、函数、还是类,都要严格的区分大小写。
  • 源文件指定编码的方式(python3默认utf8编码):
    # -*- coding: encoding -*-
    # -*- coding: gbk -*-
  • 交互模式上一次打印出来的表达式被赋值给变量 _
  • 字符串原始输出r
>>> print(r'C:\some\name')  # note the r before the quote
C:\some\name
  • 长字符串'''"""(可做多行注释):
print("""
Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to
""")

Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to
  • 开启本地HTTP服务:

    python -m http.server 7777

  • Python中字符串拼接和LPC有点类似("a""b"a+b),但不能直接和非字符串类型拼接。

  • Python除法运算符有///(整除)

  • 在 Python 中,有一个特殊的常量 None(N 必须大写)

  • 和其它程序设计语言(如 Java、C 语言)采用大括号“{}”分隔代码块不同,Python 采用代码缩进和冒号( : )来区分代码块之间的层次。

  • 重要内置函数:

    • id()函数的功能是获取变量(对象)所在的内存地址
    • type()函数的功能是获取变量的数据类型
  • Python3的变动

    • 不等于运算符 Python 3.x 中去掉了 <>,只有 != 这一种写法
    • 在 Python 3.x 中,表示八进制字面量的方式只有一种,并且必须写成“0o1000”这样的方式
    • Python 3.x 去除了 long 类型,现在只有一种整形 int(Python 整数的取值范围是无限的)
    • Python 3.x 新增了 bytes 类型,对应 Python 2.x 版本的八位串]

Python编码规范

  • 每个 import 语句只导入一个模块,尽量避免一次导入多个模块。
  • 不要在行尾添加分号,也不要用分号将两条命令放在同一行。
  • 建议每行不超过 80 个字符,如果超过,建议使用小括号将多行内容隐式的连接起来,而不推荐使用反斜杠 \ 进行连接。
  • 使用必要的空行可以增加代码的可读性,通常在顶级定义(如函数或类的定义)之间空两行,而方法定义之间空一行,另外在用于分隔某些功能的位置也可以空一行。
  • 通常情况下,在运算符两侧、函数参数之间以及逗号两侧,都建议使用空格进行分隔。

注释

Python 使用井号#作为单行注释的符号,语法格式为:

# 注释内容

Python 使用三个连续的单引号'''或者三个连续的双引号"""注释多行内容,具体格式如下:

'''
使用 3 个单引号分别作为注释的开头和结尾
可以一次性注释多行内容
这里面的内容全部是注释内容
'''

或者

"""
使用 3 个双引号分别作为注释的开头和结尾
可以一次性注释多行内容
这里面的内容全部是注释内容
"""

多行注释通常用来为 Python 文件、模块、类或者函数等添加版权或者功能描述信息。

Python标识符

  • 标识符是由字符(A~Z 和 a~z)、下划线和数字或汉字组成,但第一个字符不能是数字。
  • 标识符不能和 Python 中的保留字相同。
  • 在 Python 中,标识符中的字母是严格区分大小写的
  • Python 语言中,以下划线开头的标识符有特殊含义
    • 以单下划线开头的标识符(如 _width),表示不能直接访问的类属性,其无法通过 from...import* 的方式导入;
    • 以双下划线开头的标识符(如__add)表示类的私有成员;
    • 以双下划线作为开头和结尾的标识符(如 init),是专用标识符。

Python关键字

Python 包含的保留字可以执行如下命令进行查看:

>>> import keyword
>>> keyword.kwlist
['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

内置函数

Python 解释器也是一个程序,它给用户提供了一些常用功能,并给它们起了独一无二的名字,这些常用功能就是内置函数。Python 解释器启动以后,内置函数也生效了,可以直接拿来使用。

Python 解释器一旦启动,所有的内置函数都生效了;而导入标准库的某个模块,只是该模块下的函数生效,并不是所有的标准库函数都生效。

具体内置函数见:https://docs.python.org/zh-cn/3/library/functions.html

变量类型

Python 是弱类型的语言,弱类型语言有两个特点:

  • 变量无须声明就可以直接赋值,对一个不存在的变量赋值就相当于定义了一个新变量。
  • 变量的数据类型可以随时改变,比如,同一个变量可以一会儿被赋值为整数,一会儿被赋值为字符串。

注意,变量是标识符的一种,它的名字不能随便起,要遵守 Python 标识符命名规范,还要避免和 Python 内置函数以及 Python 保留字重名。

可以使用函数type(var)看变量类型

int

Python 整数的取值范围是无限的,不管多大或者多小的数字,Python 都能轻松处理。

  • 1) 十进制形式
  • 2) 二进制形式:0b|0B
  • 3) 八进制形式:0o|0O
  • 4) 十六进制形式0x|0X

Python 3.x 允许使用下划线_作为数字(包括整数和小数)的分隔符。通常每隔三个数字添加一个下划线,类似于英文数字中的逗号。

float

  • 1) 十进制形式
  • 2) 指数形式

    aEn 或 aen

a 为尾数部分,是一个十进制数;n 为指数部分,是一个十进制整数;E或e是固定的字符,用于分割尾数部分和指数部分。整个表达式等价于 a×10n。

注意,只要写成指数形式就是小数,即使它的最终值看起来像一个整数。例如 14E3 等价于 14000,但 14E3 是一个小数。

注意浮点数计算精度问题,如:0.1+0.2,如果需要非常精确的结果,可以使用 decimal 模块,如果 decimal 模块还是无法满足需求,还可以使用 fractions 模块

complex

复数(Complex)是 Python 的内置类型,而不依赖于标准库或者第三方库。复数由实部(real)和虚部(imag)构成,在 Python 中,复数的虚部以j或者J作为后缀,具体格式为:

a + bj

string

若干个字符的集合就是一个字符串(String)。Python 中的字符串必须由双引号" "或者单引号' '包围,具体格式为:

"字符串内容"
'字符串内容'

Python 字符串中的双引号和单引号没有任何区别。而有些编程语言的双引号字符串可以解析变量,单引号字符串一律原样输出,例如 PHPJavaScript

处理字符串中的引号

  • 1) 对引号进行转义
  • 2) 使用不同的引号包围字符串

字符串的换行:要想换行书写一个比较长的字符串,必须在行尾添加反斜杠\

Python长字符串

Python 长字符串由三个双引号"""或者三个单引号'''包围,语法格式如下:

"""长字符串内容"""
'''长字符串内容'''

在长字符串中放置单引号或者双引号不会导致解析错误。

如果长字符串没有赋值给任何变量,那么这个长字符串就不会起到任何作用,和一段普通的文本无异,相当于被注释掉了。

Python原始字符串

为了解决转义字符的问题,Python 支持原始字符串。在原始字符串中,\不会被当作转义字符,所有的内容都保持“原汁原味”的样子。

在普通字符串或者长字符串的开头加上r前缀,就变成了原始字符串,具体格式为:

str1 = r'原始字符串内容'
str2 = r"""原始字符串内容"""

如果普通格式的原始字符串中出现引号,程序同样需要对引号进行转义,否则 Python 照样无法对字符串的引号精确配对;但是和普通字符串不同的是,此时用于转义的反斜杠会变成字符串内容的一部分。在 Python 中有两种方式解决这个问题:一种方式是改用长字符串的写法,不要使用原始字符串;另一种方式是单独书写反斜杠拼接。

字符串拼接

在 Python 中拼接(连接)字符串很简单,可以直接将两个字符串紧挨着写在一起,具体格式为:

strname = "str1" "str2"

如果需要使用变量,就得借助+运算符来拼接,具体格式为:

strname = str1 + str2

字符串拼接和LPC语言用法类似,但和LPC不一样的是Python 不允许直接拼接数字和字符串,所以我们必须先将数字转换成字符串。可以借助 str()repr() 函数将数字转换为字符串。

字符串截取

  • 获取单个字符:strname[index]
  • 获取多个字符:strname[start : end : step]

当以字符串的左端(字符串的开头)为起点时,索引是从 0 开始计数的;字符串的第一个字符的索引为 0,第二个字符的索引为 1,第三个字符串的索引为 2 ……

当以字符串的右端(字符串的末尾)为起点时,索引是从 -1 开始计数的;字符串的倒数第一个字符的索引为 -1,倒数第二个字符的索引为 -2,倒数第三个字符的索引为 -3 ……

字符串常用方法

  • len():获取字符串长度或字节数
  • split():分割字符串
  • join():合并字符串
  • count():统计字符串出现的次数
  • find():检测字符串中是否包含某子串
  • index():检测字符串中是否包含某子串
  • ljust()、rjust()和center()
  • startswith()和endswith()
  • title()、lower()、upper()
  • strip()、lstrip()、rstrip()
  • format()
  • encode()和decode()
  • dir()和help()

bytes

Python bytes 类型用来表示一个字节串,类似LPC语言中的buffer类型。

字符串和 bytes 存在着千丝万缕的联系,我们可以通过字符串来创建 bytes 对象,或者说将字符串转换成 bytes 对象。有以下三种方法可以达到这个目的:

  • 如果字符串的内容都是 ASCII 字符,那么直接在字符串前面添加b前缀就可以转换成 bytes。
  • bytes 是一个类,调用它的构造方法,也就是 bytes(),可以将字符串按照指定的字符集转换成 bytes;如果不指定字符集,那么默认采用 UTF-8。
  • 字符串本身有一个 encode() 方法,该方法专门用来将字符串按照指定的字符集转换成对应的字节串;如果不指定字符集,那么默认采用 UTF-8。

bool

True 和 False 是 Python 中的关键字,当作为 Python 代码输入时,一定要注意字母的大小写,否则解释器会报错。

值得一提的是,布尔类型可以当做整数来对待,即 True 相当于整数值 1,False 相当于整数值 0。

序列

所谓序列,指的是一块可存放多个值的连续内存空间,这些值按一定顺序排列,可通过每个值所在位置的编号(称为索引)访问它们。

字符串也是一种常见的序列,它也可以直接通过索引访问字符串内的字符。

在 Python 中,序列类型包括字符串、列表、元组、集合和字典,这些序列支持以下几种通用的操作,但比较特殊的是,集合和字典不支持索引、切片、相加和相乘操作。

序列索引

序列中,每个元素都有属于自己的编号(索引)。从起始元素开始,索引值从 0 开始递增,Python 还支持索引值是负数,此类索引是从右向左计数,换句话说,从最后一个元素开始计数,从索引值 -1 开始。

序列切片

切片操作是访问序列中元素的另一种方法,它可以访问一定范围内的元素,通过切片操作,可以生成一个新的序列。

序列实现切片操作的语法格式如下:

sname[start : end : step]

其中,各个参数的含义分别是:

  • sname:表示序列的名称;
  • start:表示切片的开始索引位置(包括该位置),此参数也可以不指定,会默认为 0,也就是从序列的开头进行切片;
  • end:表示切片的结束索引位置(不包括该位置),如果不指定,则默认为序列的长度;
  • step:表示在切片过程中,隔几个存储位置(包含当前位置)取一次元素,也就是说,如果 step 的值大于 1,则在进行切片去序列元素时,会“跳跃式”的取元素。如果省略设置 step 的值,则最后一个冒号就可以省略。

序列相加

Python 中,支持两种类型相同的序列使用“+”运算符做相加操作,它会将两个序列进行连接,但不会去除重复的元素。

序列相乘

Python 中,使用数字 n 乘以一个序列会生成新的序列,其内容为原来序列被重复 n 次的结果。

检查元素是否包含在序列中

Python 中,可以使用 in 关键字检查某元素是否为序列的成员,其语法格式为:

value in sequence

和序列相关的内置函数

函数 功能
len() 计算序列的长度,即返回序列中包含多少个元素。
max() 找出序列中的最大元素。注意,对序列使用 sum() 函数时,做加和操作的必须都是数字,不能是字符或字符串,否则该函数将抛出异常,因为解释器无法判定是要做连接操作(+ 运算符可以连接两个序列),还是做加和操作。
min() 找出序列中的最小元素。
list() 将序列转换为列表。
str() 将序列转换为字符串。
sum() 计算元素和。
sorted() 对元素进行排序。
reversed() 反向序列中的元素。
enumerate() 将序列组合为一个索引序列,多用在 for 循环中。

列表(list)

创建列表
listname = [element1 , element2 , element3 , ... , elementn]

Python 还提供了一个内置的函数 list(),使用它可以将其它数据类型转换为列表类型。例如:

#将字符串转换成列表
list1 = list("hello")
print(list1)
#将元组转换成列表
tuple1 = ('Python', 'Java', 'C++', 'JavaScript')
list2 = list(tuple1)
print(list2)
#将字典转换成列表
dict1 = {'a':100, 'b':42, 'c':9}
list3 = list(dict1)
print(list3)
#将区间转换成列表
range1 = range(1, 6)
list4 = list(range1)
print(list4)
#创建空列表
print(list())
访问列表元素

使用索引访问列表元素的格式为:

listname[i]

其中,listname 表示列表名字,i 表示索引值。列表的索引可以是正数,也可以是负数。

使用切片访问列表元素的格式为:

listname[start : end : step]

其中,listname 表示列表名字,start 表示起始索引,end 表示结束索引,step 表示步长。

删除列表

对于已经创建的列表,如果不再使用,可以使用del关键字del listname将其删除。

实际开发中并不经常使用 del 来删除列表,因为 Python 自带的垃圾回收机制会自动销毁无用的列表,即使开发者不手动删除,Python 也会自动将其回收。

元组(tuple)

元组也可以看做是不可变的列表,通常情况下,元组用于保存无需修改的内容。可以理解为,tuple 元组是一个只读版本的 list 列表。

从形式上看,元组的所有元素都放在一对小括号( )中,相邻元素之间用逗号,分隔,如下所示:

tuplename = (element1, element2, ..., elementn)

除了使用( )创建元组外,Python 还提供了一个内置的函数 tuple(),用来将其它数据类型转换为元组类型。

tuple() 的语法格式如下:

tuple(data)

字典(dict)

由于字典中每个元素都包含两部分,分别是键(key)和值(value),因此在创建字典时,键和值之间使用冒号:分隔,相邻元素之间使用逗号,分隔,所有元素放在大括号{ }中。

使用{ }创建字典的语法格式如下:

dictname = {'key':'value1', 'key2':'value2', ..., 'keyn':valuen}

Python 中,还可以使用 dict 字典类型提供的 fromkeys() 方法创建带有默认值的字典,具体格式为:

dictname = dict.fromkeys(list,value=None)

其中,list 参数表示字典中所有键的列表(list);value 参数表示默认值,如果不写,则为空值 None。

还通过 dict() 映射函数创建字典。

如果要判断字典中是否存在指定键值对,首先应判断字典中是否有对应的键。判断字典是否包含指定键值对的键,可以使用 innot in 运算符。

集合(set)

在 Python 中,创建 set 集合可以像列表、元素和字典一样,直接将集合赋值给变量,从而实现创建集合的目的,其语法格式如下:

setname = {element1,element2,...,elementn}

set() 函数为 Python 的内置函数,其功能是将字符串、列表、元组、range 对象等可迭代对象转换成集合。该函数的语法格式如下:

setname = set(iteration)

其中,iteration 就表示字符串、列表、元组、range 对象等数据。

序列相关内置函数

zip函数

zip() 函数是 Python 内置函数之一,它可以将多个序列(列表、元组、字典、集合、字符串以及 range() 区间构成的列表)“压缩”成一个 zip 对象。所谓“压缩”,其实就是将这些序列中对应位置的元素重新组合,生成一个个新的元组。

reversed函数

reserved() 是 Pyton 内置函数之一,其功能是对于给定的序列(包括列表、元组、字符串以及 range(n) 区间),该函数可以返回一个逆序序列的迭代器(用于遍历该逆序序列)。

sorted函数

sorted() 作为 Python 内置函数之一,其功能是对序列(列表、元组、字典、集合、还包括字符串)进行排序。

Python缓存重用机制

Python 缓冲机制是为提高程序执行的效率服务的,实际上就是在 Python 解释器启动时从内存空间中开辟出一小部分,用来存储高频使用的数据,这样可以大大减少高频使用的数据创建时申请内存和销毁时撤销内存的开销。

id() 内置函数的功能是获取变量(对象)所在的内存地址,可以判断是否缓存了变量。

相关内置函数

  • input(): 获取用户输入的字符串
  • print(): 格式化输出字符串

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

name = "C语言中文网"
age = 8
url = "http://c.biancheng.net/"
print("%s已经%d岁了,它的网址是%s。" % (name, age, url))

Python数据类型转换

类似LPC语言,需要使用数据类型转换函数,如:int(x)float(x)

运算符

算术运算符

  • +
  • -
  • *
  • /
  • //
  • %
  • **

赋值运算符

赋值运算符=还可与其他运算符(包括算术运算符、位运算符和逻辑运算符)相结合,扩展成为功能更加强大的赋值运算符。

位运算符

  • &
  • |
  • ^
  • ~
  • <<
  • >>

关系运算符

  • > < == >= <= !=
  • is
  • is not

逻辑运算符

  • and
  • or
  • not

注意不是&& || !

and 和 or 运算符会将其中一个表达式的值作为最终结果,而不是将 True 或者 False 作为最终结果。这一点类似LPC语言。

逻辑假

  • False
  • 0和0.0
  • 对象为空或者为 None

三元运算符

Python 是一种极简主义的编程语言,它没有引入? :这个三元运算符,而是使用已有的 if else 关键字来实现相同的功能。

使用 if else 实现三目运算符(条件运算符)的格式如下:

var = exp1 if contion else exp2

Python 三目运算符支持嵌套

优先级与结合性

不要过多地依赖运算符的优先级来控制表达式的执行顺序,这样可读性太差,应尽量使用( )来控制表达式的执行顺序。

Python 中大部分运算符都具有左结合性,也就是从左到右执行;只有 ** 乘方运算符、单目运算符(例如 not 逻辑非运算符)、赋值运算符和三目运算符例外,它们具有右结合性,也就是从右向左执行。

流程控制

Python 是以缩进来标记代码块的,代码块一定要有缩进,没有缩进的不是代码块。另外,同一个代码块的缩进量要相同,缩进量不同的不属于同一个代码块。

if else 语句

可以细分为三种形式,分别是 if 语句、if else 语句和 if elif else 语句。

while循环语句

while 语句的语法格式如下:

while 条件表达式:
    代码块

for循环

for 循环的语法格式如下:

for 迭代变量 in 字符串|列表|元组|字典|集合:
    代码块

循环结构中else用法

Python 中,无论是 while 循环还是 for 循环,其后都可以紧跟着一个 else 代码块,它的作用是当循环条件为 False 跳出循环时,程序会最先执行 else 代码块中的代码。

break

continue

空语句

pass 是 Python 中的关键字,用来让解释器跳过此处,什么都不做。

推导式

推导式(又称解析器),是 Python 独有的一种特性。使用推导式可以快速生成列表、元组、字典以及集合类型的数据,因此推导式又可细分为列表推导式、元组推导式、字典推导式以及集合推导式。

列表推导式

[表达式 for 迭代变量 in 可迭代对象 [if 条件表达式] ]

元组推导式

(表达式 for 迭代变量 in 可迭代对象 [if 条件表达式] )

字典推导式

{表达式 for 迭代变量 in 可迭代对象 [if 条件表达式]}

集合推导式

{ 表达式 for 迭代变量 in 可迭代对象 [if 条件表达式] }

assert断言函数

Python assert 语句,又称断言语句,可以看做是功能缩小版的 if 语句,它用于判断某个表达式的值,如果值为真,则程序可以继续往下执行;反之,Python 解释器会报 AssertionError 错误。

assert 语句的语法结构为:

assert 条件表达式 [,描述信息]

assert 的检查是可以被关闭的,比如在命令行模式下运行 Python 程序时,加入 -O 选项就可以使程序中的 assert 失效。一旦 assert 失效,其包含的语句也就不会被执行。

函数和lambda表达式

函数

定义函数,也就是创建一个函数,可以理解为创建一个具有某些用途的工具。定义函数需要用 def 关键字实现,具体的语法格式如下:

def 函数名(参数列表):
    //实现特定功能的多行代码
    [return [返回值]]

其中,用 [] 括起来的为可选择部分,即可以使用,也可以省略。

此格式中,各部分参数的含义如下:

  • 函数名:其实就是一个符合 Python 语法的标识符,但不建议读者使用 a、b、c 这类简单的标识符作为函数名,函数名最好能够体现出该函数的功能(如上面的 my_len,即表示我们自定义的 len() 函数)。
  • 形参列表:设置该函数可以接收多少个参数,多个参数之间用逗号( , )分隔。
  • [return [返回值] ]:整体作为函数的可选参参数,用于设置该函数的返回值。也就是说,一个函数,可以用返回值,也可以没有返回值,是否需要根据实际情况而定。

为函数提供说明文档

函数的说明文档,本质就是一段字符串,只不过作为说明文档,字符串的放置位置是有讲究的,函数的说明文档通常位于函数内部、所有代码的最前面,通过调用 Python 的 help() 内置函数或者 __doc__ 属性,我们可以查看某个函数的使用说明文档。

函数参数

Python 中,根据实际参数的类型不同,函数参数的传递方式可分为 2 种,分别为值传递和引用(地址)传递:

  • 值传递:适用于实参类型为不可变类型(字符串、数字、元组);
  • 引用(地址)传递:适用于实参类型为可变类型(列表,字典);
位置参数

有时也称必备参数,指的是必须按照正确的顺序将实际参数传到函数中

关键字参数

是指使用形式参数的名字来确定输入的参数值。通过此方式指定函数实参时,不再需要与形参的位置完全一致,只要将参数名写正确即可。

默认参数

Python 允许为参数设置默认值,即在定义函数时,直接给形式参数指定一个默认值。这样的话,即便调用函数时没有给拥有默认值的形参传递参数,该参数可以直接使用定义函数时设置的默认值。

可变参数

可变参数,又称不定长参数,即传入函数中的实际参数可以是任意多个。Python 定义可变参数,主要有以下 2 种形式。

  • 1) 可变参数:形参前添加一个 '*'

此种形式的语法格式如下所示:

*args

*args 表示创建一个名为 args 的空元组,该元组可接受任意多个外界传入的非关键字实参。

  • 2) 可变参数:形参前添加两个 '*'

这种形式的语法格式如下:

**kwargs

**kwargs 表示创建一个名为 kwargs 的空字典,该字典可以接收任意多个以关键字参数赋值的实际参数。

逆向参数收集

直接将列表、元组、字典作为函数参数,Python 会将其进行拆分,把其中存储的元素按照次序分给函数中的各个形参。

类似LPC和PHP中的数组解构赋值。

None(空值)

在 Python 中,有一个特殊的常量 None(N 必须大写)。和 False 不同,它不表示 0,也不表示空字符串,而表示没有值,也就是空值。

None 有自己的数据类型,并不代表空对象,即 None 和 []、“” 不同。

return

用 def 语句创建函数时,可以用 return 语句指定应该返回的值,该返回值可以是任意类型。返回值参数可以指定,也可以省略不写(将返回空值 None)。

另外,如果 return 直接返回多个值(之间用逗号,分隔),Python 会自动将多个值封装到一个元组中,其返回值是一个元组。

partial偏函数

简单的理解偏函数,它是对原始函数的二次封装,是将现有函数的部分参数预先绑定为指定值,从而得到一个新的函数,该函数就称为偏函数。相比原函数,偏函数具有较少的可变参数,从而降低了函数调用的难度。

定义偏函数,需使用 partial 关键字(位于 functools 模块中),其语法格式如下:

偏函数名 = partial(func, *args, **kwargs)

其中,func 指的是要封装的原函数,*args**kwargs 分别用于接收无关键字实参和关键字实参。

变量作用域

  • globals()函数
  • locals()函数
  • vars(object)
  • global 语句

Python中在函数中使用全局变量和PHP很相似。

局部函数

Python 函数内部可以定义变量,这样就产生了局部变量。Python 也支持在函数内部定义函数,此类函数又称为局部函数,这一点和PHP类似。

闭包

闭包中外部函数返回的不是一个具体的值,而是一个函数。一般情况下,返回的函数会赋值给一个变量,这个变量可以在后面被继续执行调用。

lambda表达式(匿名函数)

lambda 表达式,又称匿名函数,常用来表示内部仅包含 1 行表达式的函数。如果一个函数的函数体仅有 1 行表达式,则该函数就可以用 lambda 表达式来代替。

lambda 表达式的语法格式如下:

name = lambda [list] : 表达式

其中,定义 lambda 表达式,必须使用 lambda 关键字;[list] 作为可选参数,等同于定义函数是指定的参数列表;value 为该表达式的名称。

相比函数,lamba 表达式具有以下 2 个优势:

  • 对于单行函数,使用 lambda 表达式可以省去定义函数的过程,让代码更加简洁;
  • 对于不需要多次复用的函数,使用 lambda 表达式可以在用完之后立即释放,提高程序执行的性能。

eval()和exec()函数

eval() 和 exec() 函数的功能是相似的,都可以执行一个字符串形式的 Python 代码(代码以字符串的形式提供),相当于一个 Python 的解释器。二者不同之处在于,eval() 执行完要返回结果,而 exec() 执行完不返回结果(文章后续会给出详细示例)。

eval() 函数的语法格式为:

eval(expression, globals=None, locals=None, /)

而 exec() 函数的语法格式如下:

exec(expression, globals=None, locals=None, /)

map()、filter()和reduce()

函数注解

给函数中的参数做注解的方法是在形参后添加冒号“:”,后接需添加的注解(可以是类(如 str、int 等),也可以是字符串或者表示式);给返回值做注解的方法是将注解添加到 def 语句结尾的冒号和 -> 之间。

注意,如果参数有默认值,参数注解位于冒号和等号之间。比如 eggs:str='eggs',它表示 eggs 参数的默认值为 'eggs',添加的注解为 str。

给函数定义好注解之后,可以通过函数对象的 __annotations__ 属性获取,它是一个字典,在应

类和对象

Python 中一切皆对象,面向对象的三大特征:封装、继承和多态。

Python 中定义一个类使用 class 关键字实现,其基本语法格式如下:

class 类名:
    多个(≥0)类属性...
    多个(≥0)类方法...
class Empty:
    pass

Python 类体中的代码位于独立的命名空间(称为类命名空间)中,和类命名空间相对的是全局命名空间,即整个 Python 程序默认都位于全局命名空间中,而类体则独立位于类命名空间中。

init()

构造方法用于创建对象时使用,每当创建一个类的实例对象时,Python 解释器都会自动调用它。Python 类中,手动添加构造方法的语法格式如下:

def __init__(self,...):
    代码块

即便不手动为类添加任何构造方法,Python 也会自动为类添加一个仅包含 self 参数的构造方法。仅包含 self 参数的 init() 构造方法,又称为类的默认构造方法。

Python 只是规定,无论是构造方法还是实例方法,最少要包含一个参数,并没有规定该参数的具体名称。之所以将其命名为 self,只是程序员之间约定俗成的一种习惯,遵守这个约定,可以使我们编写的代码具有更好的可读性(大家一看到 self,就知道它的作用)。

类的实例化

对已定义好的类进行实例化,其语法格式如下:

类名(参数)

使用已创建好的类对象访问类中实例变量的语法格式如下:

类对象名.变量名

使用类对象调用类中方法的语法格式如下:

对象名.方法名(参数)

注意,对象名和变量名以及方法名之间用点 "." 连接。

变量

在类体中,根据变量定义的位置不同,以及定义的方式不同,类属性又可细分为以下 3 种类型:

  1. 类体中、所有函数之外:此范围定义的变量,称为类属性或类变量;
  2. 类体中,所有函数内部:以“self.变量名”的方式定义的变量,称为实例属性或实例变量;
  3. 类体中,所有函数内部:以“变量名=变量值”的方式定义的变量,称为局部变量。
类变量(类属性)

类变量的特点是,所有类的实例化对象都同时共享类变量,也就是说,类变量在所有实例化对象中是作为公用资源存在的。类方法的调用方式有 2 种,既可以使用类名直接调用(注意:通过类名修改类变量,会作用到所有的实例化对象),也可以使用类的实例化对象调用。

除了可以通过类名访问类变量之外,还可以动态地为类和对象添加类变量。

注意,通过类对象是无法修改类变量的。通过类对象对类变量赋值,其本质将不再是修改类变量的值,而是在给该对象定义新的实例变量。

实例变量(实例属性)

实例变量指的是在任意类方法内部,以“self.变量名”的方式定义的变量,其特点是只作用于调用方法的对象。另外,实例变量只能通过对象名访问,无法通过类名访问。

类中,实例变量和类变量可以同名,但这种情况下使用类对象将无法调用类变量,它会首选实例变量,这也是不推荐“类变量使用对象名调用”的原因。

和类变量不同,通过某个对象修改实例变量的值,不会影响类的其它实例化对象,更不会影响同名的类变量。

局部变量

定义局部变量是为了所在类方法功能的实现。需要注意的一点是,局部变量只能用于所在函数中,函数执行完成后,局部变量也会被销毁。

方法

类方法、实例方法和静态方法。采用 @classmethod 修饰的方法为类方法;采用 @staticmethod 修饰的方法为静态方法;不用任何修改的方法为实例方法。

实例方法

在类中定义的方法默认都是实例方法,类的构造方法理论上也属于实例方法,只不过它比较特殊。

实例方法最大的特点就是,它最少也要包含一个 self 参数,用于绑定调用此方法的实例对象(Python 会自动完成绑定),实例方法通常会用类对象直接调用。

Python 也支持使用类名调用实例方法,但此方式需要手动给 self 参数传值。例如:

#类名调用实例方法,需手动给 self 参数传值
clang = CLanguage()
CLanguage.say(clang)
类方法

Python 类方法和实例方法相似,它最少也要包含一个参数,只不过类方法中通常将其命名为 cls,Python 会自动将类本身绑定给 cls 参数(注意,绑定的不是类对象)。

和 self 一样,cls 参数的命名也不是规定的(可以随意命名),只是 Python 程序员约定俗称的习惯而已。

和实例方法最大的不同在于,类方法需要使用@classmethod修饰符进行修饰。

类方法推荐使用类名直接调用,当然也可以使用实例对象来调用(不推荐)。

静态方法

静态方法,其实就是我们学过的函数,和函数唯一的区别是,静态方法定义在类这个空间(类命名空间)中,而函数则定义在程序所在的空间(全局命名空间)中。

静态方法没有类似 self、cls 这样的特殊参数,因此 Python 解释器不会对它包含的参数做任何类或对象的绑定。也正因为如此,类的静态方法中无法调用任何类属性和类方法。

静态方法需要使用@staticmethod修饰,例如:

class CLanguage:
    @staticmethod
    def info(name,add):
        print(name,add)

静态方法的调用,既可以使用类名,也可以使用类对象。

@property装饰器

通过 @property 装饰器,可以直接通过方法名来访问方法,不需要在方法名后添加一对“()”小括号。

@property 的语法格式如下:

@property
def 方法名(self)
    代码块

Python封装机制

Python 并没有提供 public、private 这些修饰符。为了实现类的封装,Python 采取了下面的方法:

  • 默认情况下,Python 类中的变量和方法都是公有(public)的,它们的名称前都没有下划线(_);
  • 如果类中的变量和函数,其名称以双下划线“__”开头,则该变量(函数)为私有变量(私有函数),其属性等同于 private。

事实上,Python 封装特性的实现纯属“投机取巧”,之所以类对象无法直接调用以双下划线开头命名的类属性和类方法,是因为其底层实现时,Python 偷偷改变了它们的名称。对于以双下划线开头命名的类属性或类方法,Python 在底层实现时,将它们的名称都偷偷改成了 "_类名__属性(方法)名" 的格式。

Python继承机制

子类继承父类时,只需在定义子类时,将父类(可以是多个)放在子类之后的圆括号里即可。语法格式如下:

class 类名(父类1, 父类2, ...):
    #类定义部分

注意,如果该类没有显式指定继承自哪个类,则默认继承 object 类(object 类是 Python 中所有类的父类,即要么是直接父类,要么是间接父类)。另外,Python 的继承是多继承机制(和 C++ 一样),即一个子类可以同时拥有多个直接父类。

有读者可能还听说过“派生”这个词汇,它和继承是一个意思,只是观察角度不同而已。换句话话,继承是相对子类来说的,即子类继承自父类;而派生是相对于父类来说的,即父类派生出子类。

class People:
    def say(self):
        print("我是一个人,名字是:",self.name)
class Animal:
    def display(self):
        print("人也是高级动物")
#同时继承 People 和 Animal 类
#其同时拥有 name 属性、say() 和 display() 方法
class Person(People, Animal):
    pass
zhangsan = Person()
zhangsan.name = "张三"
zhangsan.say()
zhangsan.display()

事实上,大部分面向对象的编程语言,都只支持单继承,即子类有且只能有一个父类。而 Python 却支持多继承(C++也支持多继承,LPC也支持多继承)。

和单继承相比,多继承容易让代码逻辑复杂、思路混乱,一直备受争议,中小型项目中较少使用,后来的 Java、C#、PHP 等干脆取消了多继承。

使用多继承经常需要面临的问题是,多个父类中包含同名的类方法。对于这种情况,Python 的处置措施是:根据子类继承多个父类时这些父类的前后次序决定,即排在前面父类中的类方法会覆盖排在后面父类中的同名类方法。

方法解析顺序(Method Resolution Order)

pass

异常处理机制

Python 中,用try except语句块捕获并处理异常,其基本语法结构如下所示:

try:
    可能产生异常的代码块
except [ (Error1, Error2, ... ) [as e] ]:
    处理异常的代码块1
except [ (Error3, Error4, ... ) [as e] ]:
    处理异常的代码块2
except  [Exception]:
    处理其它异常

try except else

在原本的try except结构的基础上,Python 异常处理机制还提供了一个 else 块,也就是原有 try except 语句的基础上再添加一个 else 块,即try except else结构。

使用 else 包裹的代码,只有当 try 块没有捕获到任何异常时,才会得到执行;反之,如果 try 块捕获到异常,即便调用对应的 except 处理完异常,else 块中的代码也不会得到执行。

try finally

Python 异常处理机制还提供了一个 finally 语句,通常用来为 try 块中的程序做扫尾清理工作。

注意,和 else 语句不同,finally 只要求和 try 搭配使用,而至于该结构中是否包含 except 以及 else,对于 finally 不是必须的(else 必须和 try except 搭配使用)。

在整个异常处理机制中,finally 语句的功能是:无论 try 块是否发生异常,最终都要进入 finally 语句,并执行其中的代码块。

基于 finally 语句的这种特性,在某些情况下,当 try 块中的程序打开了一些物理资源(文件、数据库连接等)时,由于这些资源必须手动回收,而回收工作通常就放在 finally 块中。

Python 垃圾回收机制,只能帮我们回收变量、类对象占用的内存,而无法自动完成类似关闭文件、数据库连接等这些的工作。

raise用法

Python 允许我们在程序中手动设置异常,使用 raise 语句即可。

raise 语句的基本语法格式为:

raise [exceptionName [(reason)]]

获取异常信息

  • sys.exc_info()方法
  • traceback模块

自定义异常类

class InputError(Exception):
    '''当输出有误时,抛出此异常'''
    #自定义异常类型的初始化
    def __init__(self, value):
        self.value = value
    # 返回异常类对象的说明信息
    def __str__(self):
        return ("{} is invalid input".format(repr(self.value)))

try:
    raise InputError(1) # 抛出 MyInputError 这个异常
except InputError as err:
    print('error: {}'.format(err))

模块和包

模块就是 Python 程序。换句话说,任何 Python 程序都可以作为模块并通过import导入。

import

import的用法主要有以下两种:

  • import 模块名1 [as 别名1], 模块名2 [as 别名2],…:使用这种语法格式的 import 语句,会导入指定模块中的所有成员(包括变量、函数、类等)。不仅如此,当需要使用模块中的成员时,需用该模块名(或别名)作为前缀,否则 Python 解释器会报错。
  • from 模块名 import 成员名1 [as 别名1],成员名2 [as 别名2],…: 使用这种语法格式的 import 语句,只会导入模块中指定的成员,而不是全部成员。同时,当程序中使用该成员时,无需附加任何前缀,直接使用成员名(或别名)即可。

注意,用 [] 括起来的部分,可以使用,也可以省略。

只要是 Python 程序,都可以作为模块导入,所以务必注意代码文件名要符合操作系统的命名规则,而且文件名不要和系统扩展包同名。如果文件名不符合命名规则,只能使用__import__()函数引入模块名。

自定义模块

if __name__ == '__main__':

可以借助 Python 内置的 __name__ 变量。当直接运行一个模块时,name 变量的值为 __main__;而将模块被导入其他程序中并运行该程序时,处于模块中的 __name__ 变量的值就变成了模块名。因此,如果希望测试函数只有在直接运行模块文件时才执行,则可在调用测试函数时增加判断,即只有当 __name__ =='__main__' 时才调用测试函数。

自定义模块编写说明文档

为自定义模块添加说明文档,和函数或类的添加方法相同,即只需在模块开头的位置定义一个字符串即可。在此基础上,我们可以通过模板的 __doc__ 属性,来访问模板的说明文档。

all变量

以“from 模块名 import *”形式导入的模块,当该模块设有 all 变量时,只能导入该变量指定的成员,未指定的成员是无法导入的。

Python包

简单理解,包就是文件夹,只不过在该文件夹下必须存在一个名为“init.py” 的文件(注意,这是 Python 2.x 的规定,而在 Python 3.x 中,init.py 对包来说,并不是必须的。)

注意,init.py 不同于其他模块文件,此模块的模块名不是 init,而是它所在的包名。例如,在 settings 包中的 init.py 文件,其模块名就是 settings。

init.py 文件的主要作用是导入该包内的其他模块(使用通配符)。

包其实本质上还是模块,因此导入模块的语法同样也适用于导入包。无论导入我们自定义的包,还是导入从他处下载的第三方包,导入方法可归结为以下 3 种:

  • import 包名[.模块名 [as 别名]]
  • from 包名 import 模块名 [as 别名]
  • from 包名.模块名 import 成员名 [as 别名]

查看模块成员:dir()函数或__all__变量。

file属性

查看模块的源文件路径。

注意,并不是所有模块都提供 file 属性,因为并不是所有模块的实现都采用 Python 语言,有些模块采用的是其它编程语言(如 C 语言)。

第三方库(模块)下载和安装

pip install|uninstall|list 模块名

文件操作(I/O)

import os

  • os.path
    • os.path.join()
import os
myFiles = ['accounts.txt', 'details.csv', 'invite.docx']
for filename in myFiles:
    print(os.path.join('C:\\demo\\exercise', filename))
  • os.getcwd()
  • os.chdir()
  • os.listdir(path)
  • os.remove(file)

open()

open() 函数用于创建或打开指定文件,该函数的常用语法格式如下:

file = open(file_name [, mode='r' [ , buffering=-1 [ , encoding = None ]]])
#以 utf-8 的编码格式打开指定文件
f = open("my_file.txt",encoding = "utf-8")
#输出读取到的数据
print(f.read())
#关闭文件
f.close()
  • file.read([size])
  • file.readlines()
  • file.readline([size])
  • file.write(string)
  • file.writelines()
  • file.close()
  • file.tell()
  • file.seek(offset[, whence])
f = open('a.txt', 'r')
n = open('b.txt','w+')
n.writelines(f.readlines())
n.close()
f.close()

with as

使用 with as 操作已经打开的文件对象(本身就是上下文管理器),无论期间是否抛出异常,都能保证 with as 语句执行完毕后自动关闭已经打开的文件。

with as 语句的基本语法格式为:

with 表达式 [as target]:
    代码块
with open('a.txt', 'a') as f:
    f.write("\nPython教程")
京ICP备13031296号-4