This repository is used to record my personal Python learning process.
2020-01-07
- 输出 "hello,world!"
print("hello,world!")
- 了解 "#!/usr/bin/env python3" 作用
-
含义
注释是为了告诉Linux
/OS X
系统,这是一个python3
可执行程序,这在电脑上同时安装了python2
和python3
的时候尤其重要, 因为python3
不向下兼容。而Windows
系统会忽略这个注释; -
env的作用
#!/usr/bin/env python3
该代码表示从
PATH 环境变量
中查找python3
解释器的位置, 路径没有被写死, 而是在环境变量
中寻找python3
解释器的安装路径, 再调用该路径下的解释器来执行脚本。#!/usr/bin/python3
该代码表示
python3
解释器所处的绝对路径就是/usr/bin/python3
, 路径被写死了, 类似于编程中的硬编码
。之所以有这种写法, 是因为在类 Unix
系统中, python 解释器一般情况下都位于这个路径。不过, 如果碰到 python 解释器不在该路径下的话, 脚本就无法执行了。显然, 采用第一种写法更灵活更具有通用性, 推荐使用这种写法。
-
- 了解 "## -*- coding: utf-8 -*-" 作用
- 含义
如果没有此文件编码类型的声明,则 python2 默认以ASCII
编码去处理;如果你没声明编码,但是文件中又包含非ASCII
编码的字符的话, python解析器去解析的 python 文件,自然就会报错了。
必须放在python文件的第一行或第二行。
声明格式要符合正则表达式"__coding[:=]\s*([-\w.]+)__"
- 含义
2020-01-07
- 函数定义:
python print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
- 参数:
objects
复数,表示可以一次输出多个对象。输出多个对象时,需要用,
分隔。sep
用来间隔多个对象,默认值是一个空格。
可以通过修改成实现取消print(sep='')
,
分隔对象产生的空格;
可以通过修改成实现逐行输出变量。print(sep='\n')
end
用来设定以什么结尾。默认值是换行符\n
。
可以通过修改成实现输出显示不自动换行。print(end='')
file
要写入的文件对象。flush
输出是否被缓存通常决定于 file,但如果flush
关键字参数为True,流会被强制刷新。
- 显示单个字符串
需使用" "
或' '
引住文字 - 显示多个字符串
使用,
符号,默认状态下显示时,
会被显示成空格,但可以通过修改print()
函数的sep
参数进行修改。除了连接多个字符串,,
还可以连接多个算式或者是连接字符串与算式;
使用+
符号,显示时字符串之间不会有空格。+
只可以连接字符串,或者在多个数字或者算式之间作运算符使用,即数学意义上的加号。但不可以连接字符串与数字或算式;
\
:- 写法:
\\
- 写法:
"
or'
:- 写法:
\" \'
- 写法:
- 换行符
\n
:- 写法:
\\n
- 写法:
- 制表符
\t
:- 写法:
\\t
- 作用:
制表符\t作用是将字符串的字数补齐成8的倍数,一般是输出表格的时候自动将各列对齐时使用的。
举例来说"abc\t",就是在"abc"后面补上5个空格,凑齐8个字符。而"abcdefghijk\t"一共11个字符,已经超过了8个字符, 但是不满足16个字符,所以\t的作用就是补上7个空格,补齐16个字符。
在某些编译器中\t不是默认占8位,可能是4位,但是一般可以手动调整。
- 写法:
r'文本'
orr"文本"
:- 作用:
在字符串引号前加上r
,可以让字符串强制不转义。当字符串中存在引号时,真正用于括字符串的引号要注意作区别,文本中存在单引号外侧就要用双引号,反之亦然。
举例:当字符串中同时存在print("I'm Peter.") # 文本含单引号,外扩双引号 print('他说:"我是彼得。"') # 文本含双引号,外扩单引号
""
和''
两种引号时,r''
和r""
都不太好用,建议单独转义。因为英文中的引号无前后之分, 字符串中的引号必然会和真正的引号混在一起,使字符串异常。
举例:print(r"I'm Peter,'他说:"我是彼得。"") # 程序会把"I'm Peter,'他说:"识别成第一个字符串,而"我是彼得。""则会识别异常; print(r'I'm Peter,'他说:"我是彼得。"') # 程序会把"I"识别成第一个字符串,而"'m Peter,'他说:"我是彼得。""则会识别异常;
- 作用:
"""文本"""
:- 作用:
字符串直接使用回车键换行,不需要在行末加\n
。
- 作用:
- 显示数字或算式
print()
函数可以接收一个变量或算式,直接显示变量的值或者是算式的结果;
- 函数定义
input(prompt) - 参数
- prompt
input()
函数中可以输入一个提示语
(prompt
),作为显示界面中提示用户输入的引导语;
- prompt
- 返回值
input()
函数输出的是字符串类型。
2020-01-09
- 分类
整数
(int
):
即整型。不仅指十进制的整数,python也可以处理其他进制的整数
python的整数没有大小限制浮点数
(float
):
即小数
python
的浮点数没有大小限制,但超过一定范围就直接表示为inf
(无限大
)字符串
(str
):- 类定义:
- str(object='')
如果encoding
或errors
均未给出,则直接返回内容为object
的字符串,object
实参需要用''
或""
括起 - str(object=b'', encoding='utf-8', errors='strict')
如果object
是一个bytes-like object
(例如bytes
或bytearray
),encoding
或errors
至少给出其中之一,否则会把object
当str
类型
str(bytes, encoding, errors)
等价于bytes.decode(encoding, errors)
- str(object='')
- 类定义:
布尔值
(bool
):- 分类:
只有True
和False
两种,首字母必须大写; - 运算:
and
(与运算
):
只有所有都为True
,and
运算结果才是True
or
(或运算
)
只要其中有一个为True
,or
运算结果就是True
not
(非运算
)
它是一个单目运算符
,把True
变成False
,False
变成True
- 分类:
空值
(None
):
空值是Python里一个特殊的值,用None
表示。None
不能理解为0,因为0是有意义的,而None
是一个特殊的空值,可以理解为没有任何东西。
- 变量名的表示方法:
变量在程序中就是用一个变量名表示了,变量名必须是大小写英文、数字和下划线的组合,且不能用数字开头; - 赋值:
等号=
是赋值语句,可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量
请不要把赋值语句的等号等同于数学的等号。比如下面的代码:如果从数学上理解x = x + 2那无论如何是不成立的,在程序中,赋值语句先计算右侧的表达式x + 2,得到结果12,再赋给变量x。由于x之前的值是10,重新赋值后,x的值变成12。x = 10 x = x + 2
- 动态语言:
这种变量本身类型不固定的语言称之为动态语言
,主要动态语言:Object-C
、C#
、JavaScript
、PHP
、Python
、Erlang
与之对应的是静态语言
,静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错。主要静态语言:Java
、C
、C++
- 变量在内存中的表示方法
当我们写:a = 'ABC'时,Python解释器干了两件事情:- 在内存中创建了一个'ABC'的字符串;
- 在内存中创建了一个名为a的变量,并把它指向'ABC'。
也可以把一个变量a赋值给另一个变量b,这个操作实际上是把变量b指向变量a所指向的数据,例如下面的代码:
程序执行过程如下:a = 'ABC' b = a a = 'XYZ' print(b)
- 执行
解释器创建了字符串'ABC'和变量a,并把a指向'ABC':a = 'ABC'
2. 执行解释器创建了变量b,并把b指向a指向的字符串'ABC':b = a
3. 执行解释器创建了字符串'XYZ',并把a的指向改为'XYZ',但b并没有更改:a = 'XYZ'
4. 所以,最后打印变量b的结果自然是'ABC'了
所谓常量就是不能变的变量,比如常用的数学常数π
就是一个常量。在python
中,通常用全部大写的变量名表示常量:
PI = 3. 14159265359
但事实上PI
仍然是一个变量,python
根本没有任何机制保证PI
不会被改变,所以,用全部大写的变量名表示常量只是一个习惯上的用法,如果一定要改变变量PI的值,并不会报错
- /除法
/除法计算结果是浮点数,即使是两个数恰好整除,结果也是浮点数。可参考以下代码:python浮点数运算遵循print(9/3) print(10/3) print(9.0/3. 0) print(10.0/3. 0)
IEEE 754
的浮点运算标准,往往存在误差,或者叫做有限精度。
参考以下算式:print(0.1+0.2) print(0.1+0.1+0.1-0.3)
- //除法
//除法称为地板除
,两个数的地板除结果是商取整,即使除不尽。具体参考以下代码:因为//除法只取结果的整数部分,所以Python还提供一个余数运算,可以得到两个整数相除的余数:print(9//3) print(10//3) print(9.0//3. 0) print(10.0//3. 0)
print(9%3) print(10%3)
2020-02-01
-
字节
由于计算机只能处理二进制数字,所以所有的文本都必须转为数字才可以交由计算机处理。因此通常由8比特
(bit
)构成1个字节
(bytes
) ,也可以理解为为每1个字符都可以对应一个8位的二进制数字。
1个字节8比特可以表示2^8
个数字(0
-255
),2个字节16比特可以表示2^16
个数字(0
-65535
),4个字节32比特可以表示2^32
个数字(0
-4294967295
) -
几种常用编码
- ASCII
全称美国信息交换标准代码
。计算机最早由美国人发明,所以一开始设计编码时只考虑到了26个英文字母大小写、数字以及一些基本符号, 合计127个字符。这个字符编码表就被称为ASCII编码表
。
由于字符数没有超出8比特的表示范围(256
),所以ASCII
码用1个字节表示1个字符。而实际上,ASCII
的字符数根本不需要8位数字, 7位即可。所以首位数字一般默认为0,只有在一些ASCII
码的变形编码上会将首位标为1,使之与原版ASCII
码作区别。 - Unicode
由于ASCII
码没有包含非英语字符,所以后续又出现了许多包含非英语字符的编码,这些编码大多与ASCII
码兼容
比如中文的GB2312
编码,与ASCII
码不同的是由于中文字符数量远大于英文字符,1个字节的范围不够表示所有字符, 所以GB2312
编码采用2字节构成1个字符。诸如此类的编码还有很多,比如日文的Shift_JIS
编码,韩文的Euc-kr
编码等等
但如果同时存在两种或以上语言的字符,采用任何一种编码都会出现乱码。针对这种情况,出现了将各种编码整合到一起的Unicode
编码, 该编码大多使用2字节构成一个字符,少部分字符需要4字节。目前大部分操作系统和编程语言都可以很好的支持Unicode
。
无论是Unicode
还是UTF-16
,都是2字节起步,所以ASCII
码需要补1个字节,即在前面补上00000000
,凑齐16比特。 - UTF-8
由于Unicode
中1个字符的字节数至少是ASCII
码的2倍,这意味着Unicode
所需的存储空间也至少是ASCII
码的2倍。 这使得文本基本以英文为主时,使用Unicode
在存贮和传输上非常浪费。
而UTF-8
码则解决了这个问题,UTF-8
码是一种可变长编码
。1个字符对应的字节数由1个到6个不等。ASCII
码中的字符采用1个字节, 与ASCII
保持一致,大部分汉字采用3个字节,部分生僻字符采用4-6个字节。在以英文字符为主时可以节省大量空间。
- ASCII
-
几种常用编码对比
字符 ASCII Unicode UTF-8 A 01000001 00000000 01000001 01000001 中 X 01001110 00101101 11100100 10111000 10101101 可以看出
ASCII
码中的字符在UTF-8
中的编码并没有变化,这意味着一些只支持ASCII
编码的软件与系统可以在UTF-8
中较好的运行。
在计算机内存中默认使用Unicode
编码,而数据传输和储存时考虑到存储体积更偏向于使用UTF-8
编码,因此在日常计算机操作中经常涉及到几种字符编码的转换,具体可以参看以下2个例子: -
python中的默认编码
python2
中默认编码是ASCII
,所以在程序开头必须带上## -*- coding: utf-8 -*-
,以保证utf-8
编码的字符可以正常显示。
python3
则默认采用Unicode
编码,具体来说就是默认用utf-8
编码去读源代码文件,而python
内存中运行中的str
是Unicode
编码
然而通过getsizeof()
方法插看str
内存大小发现,str
中ASCII
字符为1字节,非ASCII
字符为2字节, 这与Unicode
编码中所有字符都为2字节的设计有所差别。推测是因为出于节约空间的考虑,并非完全默认使用Unicode
编码, 而是将ASCII
字符都按ASCII
编码进行储存,非ASCII
字符前2^16
个字符每个2字节,往后每个4字节
检测方法:import sys for i in range(1, 200): s1 = ''.join(chr(n) for n in range(i)) s2 = ''.join(chr(n) for n in range(i + 1)) print('getsizeof{}={:d}'.format(i, sys.getsizeof(s2) - sys.getsizeof(s1))) # ASCII字符1字节 for i in range(2**16 - 10, 2**16 + 10): s1 = ''.join(chr(n) for n in range(i)) s2 = ''.join(chr(n) for n in range(i + 1)) print('getsizeof{}={:d}'.format(i, sys.getsizeof(s2) - sys.getsizeof(s1))) # 非ASCII字符中,第2^16个字符之前每个字符2字节,往后每个字符4字节
测试结果中,第
127
/255
/2^16
个字符比较异常,原因是这几个字节正好是编码中首位数字由0变1的字符,所以结构体所占的内存也会有变化
- 函数定义
ord(c) - 参数
- c
对表示单个Unicode
字符的字符串c,返回代表它Unicode码点
的int对象
。
- c
- 返回值
int
- 逆函数
chr()
- 备注
ord()
只能接收str
,所以注意str
类型的'1'和int
类型的1之间的区别
- 函数定义
- 函数定义
chr(i) - 参数
- i
返回Unicode
码位为int
对象i
的str
格式字符
i
的取值范围为0
到1,114,111
(16进制
表示是0x10FFFF
)。如果i
超过这个范围,会触发ValueError
异常。
- i
- 返回值
str
对象 - 逆函数
ord()
- 备注
int
类型的四种进制整数对chr()
来说没有区别,或者说这四种进制数对程序来说一般也没有区别。python
可以直接支持Unicode
编码,所以直接在str
对象中输入\u
+四位Unicode
编码即可打印出字符
- 函数定义
- 作用
str
类型的特殊方法,返回原字符串编码为字节串对象的版本。 - 方法定义 str.encode(encoding="utf-8", errors="strict")
- 参数
- encoding
设定encode()
返回的字节串编码方式,参数值缺省则默认为utf-8
,也可以设定为其他可用编码 - errors
设定encode()
无法对字符串全部或部分进行解码时的处理方法,参数值缺省则默认方法是strict
,即会引发UnicodeError
错误, 其他方法还包括ignore
,replace
,xmlcharrefreplace
,backslashreplace
,具体参见错误处理方案ignore
对无法解码的部分进行忽略replace
无法解码的部分用?
字符代替xmlcharrefreplace
对无法解码的部分用XML
字符引用代替backslashreplace
无法解码的部分使用带\
的转义序列进行替换namereplace
对无法解码的部分用\N{...}
转义序列进行替换
- encoding
- 返回值
bytes
对象 - 逆方法
decode()
- 备注
bytes
对象的形式为b'编码'
,具体写法由所用的字符编码决定字符 ASCII gbk UTF-8 UTF-16 A b'A' b'A' b'A' b'\xff\xfeA\x00' 中 null b'\xd6\xd0' b'\xe4\xb8\xad' b'\xff\xfe-N'
- 作用
- 作用
bytes
和bytearray
对象的附加方法,返回从给定bytes
解码出来的str
。 - 方法定义
bytes.decode(encoding="utf-8", errors="strict")
bytearray.decode(encoding="utf-8", errors="strict") - 参数
- 返回值
str
- 逆方法
encode()
- 作用
- printf风格的字符串格式化
- 格式
'%[[-][+][0][width][.precision]type]' % (obj)
只有一处占位符时可以obj
可以不加括号 - 参数
- -
左对齐,默认是右对齐 - +
正数默认显示+
符号 - 0
左侧空白位置补0 - width
整个数据输出的位数,含小数点 - .precision
对浮点数的作用是保留小数点后多少位
对字符串的作用是最大字符大小,即最多显示多少位字符串的内容 - type
用于指定占位符最后输出的字符类型,更多类型参见python
手册- %s
%s
可以接受任何类型的对象,然后转为str
,但%-0a.bf
只能识别为%-af
,即补0与.precision
会失效 - %d
%d
可以接受任何类型的数字对象,然后输出为十进制数字str
,float
对象会向-inf
取整 - %b
%b
可以接受任何类型的数字对象,然后输出为二进制数字str
,float
对象会向-inf
取整再转二进制,结果不带进制前缀 - %o
%o
可以接受任何类型的数字对象,然后输出为八进制数字str
,float
对象会向-inf
取整再转八进制,结果不带进制前缀 - %x
%x
可以接受任何类型的数字对象,然后输出为十六进制数字str
,float
对象会向-inf
取整再转二进制,结果不带进制前缀 - %f
%b
可以接受任何类型的数字对象,然后输出为浮点型数字str
,不设置.precision
参数则默认6位小数
- %s
- -
- 备注
- 对
%
转义的方式为%%
- 对
- 格式
- 格式
'{[field_name][!conversion]:[:format_spec]}'.format() - 参数
- field_name
- 作用
将占位符指向一个参数 - 格式
arg_name("."attribute_name|"["element_index"]")* - 备注
field_name
本身以一个数字或关键字arg_name
打头。如果为数字,则它指向一个位置参数,而如果为关键字,则它指向一个命名关键字参数。- 如果格式字符串中的数字
arg_names
为0,1,2,... 的序列,它们可以全部省略(而非部分省略)。数字 0, 1, 2, ... 将会按顺序自动插入。 - 由于
arg_name
不使用引号分隔,因此无法在格式字符串中指定任意的字典键 (例如字符串'10'
或':-]'
)。 arg_name
之后可以带上任意数量的arg_name.attribute_name
索引或arg_name[element_index]
属性表达式。
- 作用
- conversion
- 作用
在format()
方式对参数格式化之前对其转为字符串 - 格式 !conversion
- 转换旗标类型
- a
调用ascii()
转为ASCII
字符 - s
调用str()
转为Unicode
编码的str
字符串 - r
调用repr()
- a
- 备注
conversion
只对type
为s
的有效
- 作用
- format_spec
- 作用
format_spec
字段用于指定format()
返回值呈现的规格 - 格式
:[[fill]align][sign][#][0][width][grouping_option][.precision][type] - 参数
-
fill
- 作用
fill
参数用于接收一个字符作补位字符,不指定字符则默认是空格 - 备注
- 仅当输出的字符串宽度小于
width
时才会进行补位 - 仅当
align
是有效值时fill
参数才会有效 - 当指定了
fill
参数,则width
前的0
无效
- 仅当输出的字符串宽度小于
- 作用
-
align
- 作用
align
参数用于指定对齐方式,考虑到=
的情况,更精确的说法是指定fill
和0
参数的补位方式 - 分类
- <
强制字段在可用空间内左对齐,即在右侧补位- 备注
这是大多数对象的默认值
- 备注
- >
强制字段在可用空间内右对齐,即在左侧补位- 备注
这是数字对象的默认值
- 备注
- =
强制将fill
和0
参数指定的补位字符填充在有效的sign
参数和进制前缀之后但在数字之前- 备注
- 该方式仅对数字类型有效
- 当
0
参数有效时,它成为默认值
- 备注
- ^
强制字段在可用空间内居中对齐,即在两侧补位
- <
- 作用
-
sign
- 作用
sign
参数用于指定数字对象前的符号,不指定参数值则默认是-
- 分类
- +
表示正数和负数前要加上+
和-
符号 - -
表示只在负数前要加上-
符号 - 空格
表示应在正数前使用前加空格,在负数上使用-
- +
- 分类
- 作用
-
#
- 作用
- 对非十进制整数强制显示进制前缀
- 对浮点数、复数、十进制整数强制显示小数点符号
- 备注
仅对数字对象有效
- 作用
-
0
- 作用
使用0
来进行补位 - 备注
- 仅当输出的字符串宽度小于
width
时才会进行补位 - 当有
fill
参数,则0
无效 - 当指定了有效的
0
参数后,align
的默认值为=
,{:0d}等于{:0=d} - 由于当
type
为s
类型时,不支持=
类型的对齐方式,指定0
参数后,必须手动把align
改为其他值
- 仅当输出的字符串宽度小于
- 作用
-
width
- 作用
接收一个十进制整数,用于指定输出字符串的最小宽度,若没有指定则字段宽度将由obj
确定
- 作用
-
precision
- 格式
.precision - 作用
接收一个十进制数字,然后根据对象的type
差异作不同处理:- 对于以
f
和F
格式化的浮点数值要在小数点后显示多少个数位 - 对于以
g
或G
格式化的浮点数值要在小数点前后共显示多少个数位 - 对于非数字类型,该字段表示最大字段大小,即要使用多少个来自字段内容的字符
- 对于以
- 备注
对于整数对象不允许使用precision
- 格式
-
grouping_option
- 作用
对数字对象的每3位进行一次分割 - 分类
- ,
- _
- 对浮点表示类型和整数表示类型
d
使用下划线作为千位分隔符 - 对于整数表示类型
b
,o
,x
和X
,将为每4个数位插入一个_
- 对浮点表示类型和整数表示类型
- 备注
对于其他表示类型指定千位分隔符则将导致错误
- 作用
-
type
- 作用
确定数据应如何呈现 - 分类
具体参见格式规格迷你语言的type
参数部分
- 作用
-
- 作用
- field_name
- 格式
- printf风格的字符串格式化
2020-02-09
- 分类
- 可变序列类型
- list(列表)
- 定义
list
(列表)是可变序列,通常用于存放同类项目的集合,但也支持存放不同类型的对象到一个list
(列表)中 - 函数
- 方法
- 格式
sort(*, key=None, reverse=False) - 参数
- key
key
参数可以接收一个函数,该函数必须可以接收一个参数并返回一个值,list
对象中的元素会依次输入该函数,并将返回值作为排序对象
list
对象自身不可以作key
参数,但可以用深浅拷贝来做参数
默认值None
表示直接对列表项排序而不计算每一个单独的键 - reverse
reverse
为一个布尔值。如果设为True
,则每个列表元素将按反向顺序比较进行排序。默认为False
- key
- 备注
- 此方法会原地修改序列以保证空间经济性.此操作是通过间接影响进行的,它并不会返回排序后的序列
- 如需要返回一个已排序列表对象,请使用
sorted()
显示地请求一个新的已排序列表 sort()
方法确保是稳定的。如果一个排序确保不会改变比较结果相等的元素的相对顺序就称其为稳定的,这有利于进行多重排序。
- 格式
- 格式
result = [obj.method() if …… else obj.method() for obj in mylist if ……] - 备注
for in
前面也可以接一个if else
表达式,按情况输出不同的obj.method()
for in
后面接了if
条件句,用于筛选可迭代对象中的元素
- 格式
- 定义
- list(列表)
- 不可变序列类型
- tuple(元组)
- 定义
tuple
(元组)是不可变序列,通常用于储存异构数据的多项集(例如由enumerate()
内置函数所产生的二元组)。
tuple
也被用于需要同构数据的不可变序列的情况(例如允许存储到set
或dict
的实例)。 - 函数
- 备注
- 决定生成元组的其实是逗号而不是圆括号。圆括号只是可选的,生成空元组或需要避免语法歧义的情况除外
- 单元素元组的表示方法是
a,
或(a,)
,而非a
和(a)
- 定义
- range
- 定义
range
类型表示不可变的数字序列,通常用于在for
循环中循环指定的次数。 - 函数
- 格式
[start = 0,] stop[, step = 1]- 参数
- start
设定range
对象的起始值,参数缺省则默认从0开始 - stop
range
对象值的上限,且不可以达到这个值 - step
range
对象的步长,参数缺省则默认为1,如果step
为0则会引发ValueError
- start
- 备注
- 如果
step
为正值,确定range
对象r内容的公式为r[i] = start + step *i
其中i >= 0
且r[i] < stop
- 如果
step
为负值,确定range
对象r内容的公式仍然为r[i] = start + step * i
,但限制条件改为i >= 0
且r[i] > stop
- 如果
r[0]
不符合值的限制条件,则该range
对象为空。 range
对象虽然支持负索引,但是会将其解读为从正索引所确定的序列的末尾开始索引。- 元素绝对值大于
sys.maxsize
的range
对象是被允许的,但某些特性(例如len()
)可能引发OverflowError
- 使用
==
和!=
检测range
对象是否相等是将其作为序列来比较。也就是说,如果两个range
对象表示相同的值序列就认为它们是相等的。 (请注意比较结果相等的两个range
对象可能会具有不同的start
,stop
和step
属性 例如:
range(0) == range(2, 1, 3) range(0, 3, 2) == range(0, 4, 2)
- 如果
- 参数
- 格式
- 定义
- tuple(元组)
- 可变序列类型
- 三种类型区别
list
对象为可变类型对象,tuple
和range
为不可变类型对象range
比tuple
占用更少的内存,因为range
总是占用固定数量的内存,不论其所表示的范围有多大(因为它只保存了start
,stop
和step
值, 并会根据需要计算具体单项或子范围的值)
- 操作
- 序列类型通用操作
- x in s
- 结果
如果s中的某项等于x则结果为True
,否则为False
- 备注
- 对其他序列类型也适用,如
str
、bytes
、btyearray
- 对其他序列类型也适用,如
- 结果
- x not in s
- 结果
如果s中的某项等于x则结果为False
,否则为True
- 备注
- 对其他序列类型也适用,如
str
、bytes
、btyearray
- 对其他序列类型也适用,如
- 结果
- s + t
- 结果
序列对象s与t拼接 - 备注
- s和t中有可变类型元素时会产生浅拷贝问题
range
不支持拼接操作- s + t的拼接方式每次都会生成一个新对象,所以大批量处理时的效率比较低建议替换成别的方法
- 可变序列类型(list)
- 替代方法
- s.extend()
- s += t
for in
循环 + s.append()
- 效率比较
s = s + t
>>>s.append()
>s.extend()
=s += t
四种list拼接方法运行时间对比:
三种list拼接方法运行时间对比:
- 不可变序列类型
- str
str.join([str1, ...,strn])
io.StringIO
- bytes
bytes.join([bytes1, ...,bytesn])
io.BytesIO
- 改用可变类型对象
bytearray
- tuple
- 改用可变类型对象
list
- 结果
- s * n
- 结果
序列对象s的n次拼接 - 备注
range
对象不支持拼接操作n <= 0
则识别为0,会返回一个和s同类型的空序列- s中有可变类型元素时会产生浅拷贝问题
- 结果
- s[i]
- 结果
s的i位置元素 - 备注
- i从0开始计算
- i为负数则索引顺序是相对于s的末尾。索引号会被替换为
len(s) + i
- 但要注意不存在
s[len(s)]
,所以-0仍然为0
- 结果
- s[i:j:k]
- 结果
s从i到j位置步长为k的切片,即所有满足0 <= n < (j-i)/k
的索引号x = i + n*k
的项(x < j
)组成的序列 - 备注
- 当k为正值时
- 若i或j的值大于
len(s)
时,只能识别为len(s)
- i与j须遵守
i <= j
, i或j为负数则索引顺序是相对于s的末尾,索引号需被替换为len(s) + i
或len(s) + j
进行判断大小,否则返回[]
s.[i:i:1] = t
等价于s.[i:i] = t
(0 <= i <= len(s)
),表示在i位置插入序列t, i为len(s)
表示在序列最后插入- 当i或j的值缺省时,i默认为0,j默认为
len(s) - 1
- 当k为负值时
- 若i或j的值大于
len(s) - 1
时,只能识别为len(s) - 1
,所以k为负数时无法用s.[len(s):len(s):k] = t
进行序列插入 - i与j须遵守
i >= j
, i或j为负数则索引顺序是相对于s的末尾,索引号需被替换为len(s) + i
或len(s) + j
进行判断大小,否则返回[]
- 当i或j的值缺省时,i默认为
len(s) - 1
,j默认为0
- k不可为0,k值缺省或为None时默认为1
- 结果
- len(s)
- 结果
s的长度
- 结果
- max(s)
- 结果
s的最大项
- 结果
- min(s)
- 结果
s的最小项
- 结果
- s.index(x[, i[, j]])
- 结果
x在s中大于等于i且小于j的范围中首次出现的位置 - 备注
- 当x在s中找不到时
index
会引发ValueError
- 不是所有实现都支持传入额外参数i和j,但这两个参数允许高效地搜索序列的子序列,等价于
s[i:j].index(x)
- 该操作不会复制任何数据,并且返回的索引是相对于序列的开头而非切片的开头
- 当x在s中找不到时
- 结果
- s.count(x)
- 结果
x在s中出现的总次数
- 结果
- x in s
- 可变序列类型操作
- s[i] = x
- 结果
s的第i项赋值为x
- 结果
- s[i:j] = t
- 结果
s从i到j位置的切片替换成可迭代对象t - 备注
s[:] = []
表示清空s,等价于s.clear()
和del s[:]
s.[i:i] = t
(0 <= i <= len(s)
),表示在i位置插入序列t, i为len(s)
表示在序列最后插入
- 结果
- del s[i:j]
- s[i:j:k] = t
- 结果
所有满足0 <= n < (j-i)/k
的索引号x = i + n*k
的项(x < j
)依次替换成t中的元素 - 备注
- 不同于
s[i:j] = t
的整段覆盖,s[i:j:k] = t
中k绝对值不为1时,其元素是间隔排列的,所以不能直接整段替换成t, 必须严格遵循元素一一替换,因此要求len(s[i:j:k]) == len(t)
- 不同于
- 结果
- s.append(x)
- 结果
在s末端插入x,等价于s[len(s):len(s)] = [x]
- 结果
- s.clear()
- 结果
清除s中的所有项,等价于s[:] = []
和del s[:]
- 备注
s.clear()
是为了与不支持切片操作的可变容器(例如dict
和set
)的接口保持一致。
- 结果
- s.copy()
- 结果
创建一个s的浅拷贝,等同于copy.copy(s)
、s[:]
和list(s)
- 备注
s.copy()
是为了与不支持切片操作的可变容器(例如dict
和set
)的接口保持一致。copy()
不是collections.abc.MutableSequence ABC
的一部分,但大多数具体的可变序列类都提供了它
- 结果
- s.extend(t)或s += t
- s *= n
- s.insert(i, x)
- 结果
在s的i位置插入x元素 - 备注
- 等同于
s[i:i] = [x]
- 等同于
- 结果
- s.pop(i)
- s.remove(x)
- 结果
删除s中第一个值等于x的元素 - 备注
- 当在s中找不到x时
remove()
操作会引发ValueError
- 当在s中找不到x时
- 结果
- s.reverse()
- 结果
就地将列表中的元素逆序 - 备注
s.reverse()
不是创建s的逆序副本,而是对s自身进行修改s.reverse()
并不会返回反转后的序列
- 结果
- s[i] = x
- 序列类型通用操作
2020-02-12
- 常用方法
identifier = object
- 解释
使一个变量(官方称之为标识符(identifier
))建立起与对象的引用 - 备注
- 与
C
、C++
等语言不同,变量(标识符)本身无类型,与之绑定的对象有类型 =
是赋值的关键,大部分情况下有无严格的identifier =
可以区分操作是修改对象还是再赋值, 比如s = s + t
和s += t
、s = s * t
和s *= t
、s.expend(t)
和s = s + t
请看下面这个例子:
def func(m): m[0] = 20 m = [4, 5, 6] return m l = [1, 2, 3] func(l) print('l =', l)
l被赋值为
[1, 2, 3]
,所以在func(l)
在传参时x也被赋值为func(l)
,m[0] = 20
将被引对象修改为[20, 2, 3]
, l因为引用同一个对象所以也同步更新为[20, 2, 3]
,而m = [4, 5, 6]
则是对m的重新赋值,所以m和原list对象[20, 2, 3]
脱离关系
因为l不受m的再次赋值的影响,所以最终的值为[20, 2, 3]
- 函数传参数就相当于对形参进行赋值,而非
C
、C++
意义上的传值或传引用 - 赋值的过程如下面的代码与图片所示:
a = 1 print('a', a, id(a)) b = 2 print('b', b, id(b)) c = 1 print('c', c, id(c)) # 再次赋值 a = b print('a', a, id(a))
输出结果: a 1 4301490544 b 2 4301490576 c 1 4301490544 a 2 4301490576
- 与
- 常用方法
import copy
copy.copy()
- 解释
浅拷贝生成的对象自身使用新的内存地址,而其一维元素和更高维的元素仍是被拷贝对象的引用 - 备注
- 常用方法
import copy
copy.deepcopy()
- 解释
深拷贝生成的对象自身、一维元素和更高维的元素都使用新的内存地址 - 备注
- 当被引对象中存在多个项引用同一对象的情况,请参看下面的代码:
import deepcopy a = [3, 4] m = [1, 2, a, [5, a]] n = copy.deepcopy(m) n[3][1][0] = -1 print(n) print(m) print(a)
输出结果: [1, 2, [-1, 4], [5, [-1, 4]]] [1, 2, [3, 4], [5, [3, 4]]] [3, 4]
m中的a只是起到引用同一个
list
对象[3, 4]
的作用,可以理解为a只是这个list对象的别名,他俩是绑定关系
deepcopy()
对各层元素都会建立副本,所以m[2]
和m[3][1]
指向的都是list对象[3, 4]
的副本,而非本体
所以m[2]
和m[3][1]
和原本的list对象[3, 4]
以及a都没有关系了
2020-02-20
- 结构
if a: do a1 elif b: do b1 else: do c1
- 说明
if
语句通过对表达式逐个求值直至找到一个真值在子句体中选择唯一匹配的一个,然后执行该子句体的执行语句。而其他部分不会被执行或求值。 如果所有表达式均为假值,且存在else
子句体,就会执行else
。 - 备注
- if语句自上而下逐行运行,当判断语句为
True
时跳入当前分支的执行语句,后面的分支不再执行。因此各分支的判断范围必须彼此独立, 或者范围逐渐扩大。 - 判断语句必须以
:
结尾,执行语句必须缩进 elif
和else
语句是可省略的- 判断语句如果是非零数值、非空字符串、非空
list
等,就判断为True
,否则为False
- if语句自上而下逐行运行,当判断语句为
- 分类
- 循环相关语法
- 备注
- 循环语句的条件语句都要以
:
结尾,执行语句缩进 continue
和break
一般都是配合if
使用。但不可以滥用break
和continue
会造成代码执行逻辑分叉过多,容易出错。 大多数循环并不需要用到break
和continue
语句,通常可以通过改写循环条件或者修改循环逻辑,去掉break
和continue
语句- 出现死循环可以使用
ctrl + c
强制结束程序 else
语句可选
- 循环语句的条件语句都要以
2020-02-21
- 定义
字典是另一种采用key-value
(键-值方法)储存可变容器模型,且可存储任意类型对象。
字典的每个键-值key=>value
对用冒号:
分割,每个键-值对之间用逗号,
分割,整个字典包括在花括号{}
中 - 函数
- 格式
dict(**kwarg)
dict(mapping, **kwarg)
dict(iterable, **kwarg) - 备注
从函数的三种格式可知,
dict()
支持三种方式输入参数:- 直接通过任意数量的关键词(
**kwarg
)参数传参,比如dict(subject: Math, teacher: Jack, classroom: 401)
- 通过
map()
、zip()
函数等可映射对象(mapping
)传参,比如dict(zip(list1, list2))
- 通过可迭代(
iterable
)对象传参,参数需要以可迭代的键-值对元组的形式输入, 如dict([(subject, Math), (teacher, Jack), (classroom, 401)])
mapping
传参可以和关键词传参方式混用,iterable
传参也可以和关键词传参方式混用
- 直接通过任意数量的关键词(
- 格式
- 操作
- list(dict)
- 结果
将dict
对象输入list
构造器可以输出一个以dict
的键为元素的list
- 结果
- len(dict)
- 结果
返回dict
对象中项的个数
- 结果
- dict[key]
- 结果
返回dict
中以key
为键的项。 如果映射中不存在key
则会引发KeyError
- 结果
- dict[key] = value
- 结果
对键
key
的项,将其值设为value
- 结果
对键
- del dict
- 结果
删除dict
对象
- 结果
- del dict[key]
- 结果
删除dict
对象键为key
的项
- 结果
- dict.keys()
- 结果
返回以dict
对象的键为元素的list
对象
- 结果
- dict.values()
- 结果
返回以dict
对象的值为元素的list
对象
- 结果
- dict.items()
- 结果
返回以dict
对象的键-值对元组为元素的list
对象
- 结果
- dict.copy()
- 结果
创建dict
对象的浅拷贝
- 结果
- dict.clear()
- 结果
清除list
中的所有元素
- 结果
- reversed(dict)
- 结果
返回一个以dict
的键为元素逆序排列的iterable
对象
- 结果
- iter(dict)
- 结果
返回一个以dict
的键为元素的iterable
对象
- 结果
- dict.fromkeys(seq[, value])
- 结果
返回一个以iterable
的seq
参数为键,value
参数为值的dict
对象,所以与该方法绑定的dict
对象没什么关系
- 结果
- dict.get(key, default)
- 结果
dict
中有key
元素则返回key
的值,没有则返回default
,不会像dict[key]
那样触发报错 - 备注
default
默认为None
- 结果
- dict.pop(x)
- 结果
去除键为x的项,并返回该项的值
- 结果
- dict.popitem()
- 结果
按照LIFO
(先进后出)的顺序删去最后一项,并返回该键-值对元组
- 结果
- dict.setdefault(x, default)
- 结果
如果存在键为x的对象则返回键的值,不存在则建立该键并赋值为default
- 备注
default
默认为None
- 结果
- dict.update()
- 结果
使用update()
的参数来更新dict
,已存在的键会更新对应的值,不存在的键会直接建立并赋值 - 备注
dict
对象的常用拼接方法
- 结果
- x (not) in dict
- 结果
(没)有键为x的项返回True
,否则返回False
- 结果
- list(dict)
- 字典推导式
- 格式
{key: value if …… else key: value for key, value in …… if ……} - 备注
for in
后面要接一个元素为键-值对的可迭代对象,value
和key
分别对应键和值- 也可以通过
zip()
函数缝合两个可迭代对象,在for in
分别为value
和key
赋值 for in
前面也可以接一个if else
表达式,按情况输出不同的value
和key
for in
后面接了if
条件句,用于筛选可迭代对象中的元素key: value
部分可以对键-值做一些运算,然后以运算结果作为最终的键-值
- 格式
- 备注
dict
对象的键几乎可以是任何值,除了非hashable
的值,如列表、字典或其他可变类型的值。- 数字类型用作键时遵循数字比较的一般规则:如果两个数值相等(例如1和1.0)则两者可以被用来索引同一字典条目(但是请注意, 由于计算机对于浮点数存储的只是近似值,因此将其用作字典键是不明智的。)
dict
对象因为采用key-value
(键-值方法)储存,查找和插入的速度极快,不会随着key
的增加而变慢。但是需要占用大量的内存, 内存浪费多- 两个字典的比较当且仅当它们具有相同的键-值对时才会相等(不考虑顺序)。排序比较
<
,<=
,>=
,>
会引发TypeError
dict
对会保留插入时的顺序。请注意对键的更新不会影响顺序。删除并再次添加的键将被插入到末尾
- 定义
集合对象是由具有唯一性的hashable
对象所组成的无序多项集
常见的用途包括成员检测、从序列中去除重复项以及数学中的集合类计算,例如交集、并集、差集与对称差集等 - 分类
- set类型
- 定义
set
类型是可变的,其内容可以使用add()
和remove()
这样的方法来改变。由于是可变类型,它是非hashable
的, 且不能被用作dict
的key
或其他集合对象的元素 - 函数
set([iterable])- 备注
- 想要创建一个空
set
对象必须借助其构造器函数,直接使用{}
会被识别为dict
对象 - 返回一个新的
set
对象,其元素来自于iterable
。集合的元素必须为hashable
- 要表示由集合对象构成的
set
对象,所有的内层集合必须为frozenset
对象。如果未指定iterable
,则将返回一个新的空集合
- 想要创建一个空
- 备注
- 定义
- frozenset类型
- 定义
frozenset
类型是不可变并且为hashable
,其内容在被创建后不能再改变。因此它可以被用作dict
的key
或其他集合的元素 - 函数
frozenset([iterable])- 备注
- 想要创建一个空
frozenset
对象必须借助其构造器函数,直接使用{}
会被识别为dict
对象 - 返回一个新的
frozenset
对象,其元素来自于iterable
。集合的元素必须为hashable
- 要表示由集合对象构成的
frozenset
对象,所有的内层集合必须为frozenset
对象。如果未指定iterable
, 则将返回一个新的空集合
- 想要创建一个空
- 备注
- 定义
- set类型
- 操作
- 集合对象通用操作
- len(set)
- 结果
返回集合对象中项的个数(即对象的基数)
- 结果
- set.copy()
- 结果
返回集合对象的浅拷贝
- 结果
- x in set
- 结果
如果集合对象中的某项等于x
则结果为True
,否则为False
- 结果
- x not in set
- 结果
如果集合对象中的某项等于x
则结果为False
,否则为True
- 结果
- A.isdisjoint(B)
- 结果
验证A
集合和B
可迭代对象是否没有交集,没有则返回True
,反之则False
- 备注
B
并不一定要集合对象,只要是可迭代对象即可,执行该方法的时候会将B
隐性地转为集合对象进行比较
- 结果
- A.issubset(B)
- 结果
验证A集合是否是B可迭代对象的子集,等价于set1 <= set2
- 备注
B
并不一定要集合对象,只要是可迭代对象即可,执行该方法的时候会将B
隐性地转为集合对象进行比较
- 结果
- set1 <= set2
- 结果
验证集合set1
是否是set2
的子集,等价于A.issubset(B)
- 备注
- 受集合对象比较法则的约束,集合对象只可以和集合对象比较大小,
set
和frozenset
可以互相比较, 这是与A.issubset(B)
方法最大的区别
- 受集合对象比较法则的约束,集合对象只可以和集合对象比较大小,
- 结果
- set1 < set2
- 结果
验证集合set1
是否是set2
的真子集,即set <= other and set != other
- 备注
- 受集合对象比较法则的约束,集合对象只可以和集合对象比较大小,
set
和frozenset
可以互相比较
- 受集合对象比较法则的约束,集合对象只可以和集合对象比较大小,
- 结果
- A.issuperset(B)
- 结果
验证A
集合是否是B
可迭代对象的超集,等价于set1 >= set2
- 备注
B
并不一定要集合对象,只要是可迭代对象即可,执行该方法的时候会将B
隐性地转为集合对象进行比较
- 结果
- set1 >= set2
- 结果
验证集合set1
是否是set2
的超集,等价于A.issuperset(B)
- 备注
- 受集合对象比较法则的约束,集合对象只可以和集合对象比较大小,
set
和frozenset
可以互相比较, 这是与A.issuperset(B)
方法最大的区别
- 受集合对象比较法则的约束,集合对象只可以和集合对象比较大小,
- 结果
- set1 > set2
- 结果
验证集合set1
是否是set2
的真超集,即set >= other and set != other
- 备注
- 受集合对象比较法则的约束,集合对象只可以和集合对象比较大小,
set
和frozenset
可以互相比较
- 受集合对象比较法则的约束,集合对象只可以和集合对象比较大小,
- 结果
- A.union(B1, B2, ...,Bn)
- 结果
返回集合对象A
和可迭代对象B1-Bn
的并集 - 备注
B1-Bn
并不一定要集合对象,只要是可迭代对象即可,执行该方法的时候会将B1-Bn
隐性地转为集合对象进行并集操作
- 结果
- set1 | set2 | …… | setn
- 结果
返回集合对象set1
到setn
的并集 - 备注
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
set
和frozenset
可以互相使用运算符运算, 这是与A.union(B1, B2, ...,Bn)
方法最大的区别
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
- 结果
- A.intersection(B1, B2, ...,Bn)
- 结果
返回集合对象A
和可迭代对象B1-Bn
的交集 - 备注
B1-Bn
并不一定要集合对象,只要是可迭代对象即可,执行该方法的时候会将B1-Bn
隐性地转为集合对象进行交集操作
- 结果
- set1 & set2 & …… & setn
- 结果
返回集合对象set1
到setn
的交集 - 备注
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
set
和frozenset
可以互相使用运算符运算, 这是与A.intersection(B1, B2, ...,Bn)
方法最大的区别
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
- 结果
- A.difference(B1, B2, ...,Bn)
- 结果
返回集合对象A
和可迭代对象B1-Bn
的差集 - 备注
B1-Bn
并不一定要集合对象,只要是可迭代对象即可,执行该方法的时候会将B1-Bn
隐性地转为集合对象进行差集操作
- 结果
- set1 - set2 - …… - setn
- 结果
返回集合对象set1
到setn
的差集 - 备注
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
set
和frozenset
可以互相使用运算符运算, 这是与A.difference(B1, B2, ...,Bn)
方法最大的区别
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
- 结果
- A.symmetric_difference(B)
- 结果
返回集合对象A
和可迭代对象B
的对称差集 - 备注
B
并不一定要集合对象,只要是可迭代对象即可,执行该方法的时候会将B
隐性地转为集合对象进行对称差集操作
- 结果
- set1 ^ set2 ^ …… ^ setn
- 结果
返回集合对象set1
到setn
的对称差集 - 备注
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
set
和frozenset
可以互相使用运算符运算, 这是与A.symmetric_difference(B1, B2, ...,Bn)
方法是不同的 - 该运算支持同时计算多个集合对象的对称差集,但是
A.symmetric_difference(B)
只支持两个对象求对称差集
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
- 结果
- len(set)
- set类型操作
- A.add(x)
- 结果
在set
对象A中插入x元素
- 结果
- A.remove(x)
- 结果
set
对象A
中存在x
元素则消除该元素,不存在则报错 - 备注
set.remove()
方法支持输入set
对象,等同于同内容的frozenset
对象
- 结果
- A.discard(x)
- 结果
set
对象A
中存在x
元素则消除该元素,不存在不会报错 - 备注
set.discard()
方法支持输入set
对象,等同于同内容的frozenset
对象
- 结果
- A.pop()
- 结果
随机消除set
对象中的一个元素(因为set
对象中的元素没有顺序的概念)
- 结果
- A.clear()
- 结果
清除set
对象中所有的元素
- 结果
- A.update(B1, B2, ...,Bn)
- 结果
set
类型版的A.union(B1, B2, ...,Bn)
,可直接对原对象进行修改 - 备注
B1-Bn
并不一定要集合对象,只要是可迭代对象即可,执行该方法的时候会将B1-Bn
隐性地转为集合对象进行并集操作
- 结果
- set1 |= set2 | …… | setn
- 结果
set
类型版的set1 | set2 | …… | setn
,可直接对原对象进行修改 - 备注
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
set
和frozenset
可以互相使用运算符运算, 这是与A.update(B1, B2, ...,Bn)
方法最大的区别
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
- 结果
- A.intersection_update(B1, B2, ...,Bn)
- 结果
set
类型版的A.intersection(B1, B2, ...,Bn)
,可直接对原对象进行修改 - 备注
B1-Bn
并不一定要集合对象,只要是可迭代对象即可,执行该方法的时候会将B1-Bn
隐性地转为集合对象进行交集操作
- 结果
- set1 &= set2 & …… & setn
- 结果
set
类型版的set1 & set2 & …… & setn
,可直接对原对象进行修改 - 备注
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
set
和frozenset
可以互相使用运算符运算, 这是与A.intersection_update(B1, B2, ...,Bn)
方法最大的区别
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
- 结果
- A.difference_update(B1, B2, ...,Bn)
- 结果
set
类型版的A.difference(B1, B2, ...,Bn)
,可直接对原对象进行修改 - 备注
B1-Bn
并不一定要集合对象,只要是可迭代对象即可,执行该方法的时候会将B1-Bn
隐性地转为集合对象进行差集操作
- 结果
- set1 -= set2 | …… | setn
- 结果
set
类型版的set1 - set2 - …… - setn
,可直接对原对象进行修改 - 备注
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
set
和frozenset
可以互相使用运算符运算, 这是与A.difference_update(B1, B2, ...,Bn)
方法最大的区别 - 由于在运算符优先级中
-
高于-=
,所以set1 -= set2 - …… - setn
的写法是不行的,相当于set1 -= (set2 - …… - setn)
, 所以要改为set1 -= set2 | …… | setn
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
- 结果
- A.symmetric_difference_update(B)
- 结果
set
类型版的A.symmetric_difference(B)
,可直接对原对象进行修改 - 备注
B
并不一定要集合对象,只要是可迭代对象即可,执行该方法的时候会将B
隐性地转为集合对象进行对称差集操作
- 结果
- set1 ^= set2 ^ …… ^ setn
- 结果
set
类型版的set1 ^ set2 ^ …… ^ setn
,可直接对原对象进行修改 - 备注
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
set
和frozenset
可以互相使用运算符运算, 这是与A.symmetric_difference_update(B1, B2, ...,Bn)
方法是不同的 - 该运算支持同时计算多个集合对象的对称差集,但是
A.symmetric_difference_update(B)
只支持两个对象求对称差集
- 受集合对象运算法则的约束,集合对象只可以和集合对象使用运算符运算,
- 结果
- A.add(x)
- 集合对象通用操作
- 备注
- 作为一种无序的多项集,集合对象并不记录元素位置或插入顺序。相应地,集合不支持索引、切片或其他序列类对象的操作
- 集合对象是可遍历的,因此可以使用
for i in set/frozenset
这样的遍历语句 - 集合对象进行
|
/|=
/&
/&=
/-
/-+
/^
/^=
等集合对象运算符操作时,如果参与运算的集合对象同时包括set
和frozenset
, 则结果的类型取参与运算的第一个对象的类型 set
和frozenset
均支持集合与集合的比较。两个集合当且仅当各为对方的子集时则相等。一个集合当且仅当其为另一个集合的真子集时则小于另一个集合。 一个集合当且仅当其为另一个集合的真超集时则大于另一个集合set
的实例与frozenset
的实例之间基于它们的成员进行比较。例如set('abc') == frozenset('abc')
返回True``set('abc') in set([frozenset('abc')])
也一样- 子集与相等比较并不能推广为完全排序函数。例如任意两个非空且不相交的集合不相等且互不为对方的子集,因此
a<b
、a==b
或a>b
比较均返回False
- 由于集合仅定义了部分排序(子集关系),因此由集合构成的列表
list.sort()
方法的输出并无定义
2020-02-26
- 格式
- 规则
- 定义一个函数要使用
def
语句,依次写出函数名、括号()
、括号中的参数和冒号:
- 然后,在缩进块中编写函数体
- 函数的返回值用
return
语句返回
- 定义一个函数要使用
- 结构示例
# 前面空两行 def 函数名(参数): 函数体 reture 返回值 # 后面空两行
- 备注
- 函数体内部的语句在执行时,一旦执行到
return
时,函数就执行完毕,并将结果返回。因此,函数内部通过条件判断和循环可以实现非常复杂的逻辑 - 函数如果没有
return
语句,函数执行完毕后也会返回结果,只是结果为None
return None
可以简写为return
return
多个值,会以tuple
的形式返回- 如果想定义一个什么事也不做的空函数,可以在函数体中用
pass
语句。pass
也可以用来作为占位符, 比如现在还没想好怎么写函数的代码,就可以先放一个pass
,让代码能运行起来
- 函数体内部的语句在执行时,一旦执行到
- 规则
- 检查参数个数
调用函数时,如果参数个数不对,Python
解释器会自动检查出来,并抛出TypeError
- 检查参数类型
如果参数类型不对,Python
解释器无法帮我们检查,必须自己设置报错机制。思路为isintance()
+raise
- 报错设置方法
- 格式
isinstance(object, classinfo) - 作用
如果参数object
是参数classinfo
的实例或者是其(直接、间接或虚拟)子类则返回True
。如果object
不是给定类型的对象,函数将总是返回False
- 备注
- 如果
classinfo
是类型对象元组(或由其他此类元组递归组成的元组),那么如果object
是其中任何一个类型的实例就返回True
- 如果
classinfo
既不是类型,也不是类型元组或类型元组的元组,则将引发TypeError
异常
- 如果
- 格式
- 作用
raise
可以显式地激活最近的一个异常,然后结束函数 - 格式
raise [expression [from expression]] - 参数
- expression
raise
会将第一个expression
求值为异常对象。它必须为BaseException
的子类或实例 - from
用于连接多个expression
- expression
- 备注
- 如果当前作用域内没有激活的异常,将会引发
RuntimeError
来提示错误 - 如果不带表达式,
raise
会重新引发当前作用域内最后一个激活的异常
- 如果当前作用域内没有激活的异常,将会引发
- 作用
- 报错设置方法
- 本文件调用自定义函数
定义完自定义函数后就可以像内置函数一样直接使用 - 跨文件使用自定义函数
- 方法
- from filename import function
- import function
- 备注
filename
是指自定义函数所在文件的文件名,不含后缀function
只需要写函数名,不需要带()
- 方法
2020-02-28
- 必选参数
也可以理解为位置或关键词参数,是函数形参的默认形式,可以通过位置匹配或关键词匹配(
key
=value
)两种方式赋值- 分类
- 位置参数
作为位置函数的实参是通过位置次序与定义函数时的位置形参进行匹配 - 关键词参数
作为关键词函数的实参通过关键词与同名关键词的形参进行匹配,无需遵循位置匹配规则 - 默认参数
必选参数的一种特殊形式,在定义函数时给参数设置了默认值,当调用函数时没有指定该参数的实参,则将该参数取默认值
默认参数可以有效地降低函数的调用难度
- 位置参数
- 备注
- 必选参数部分如果实参混用位置参数与关键词参数,必须先写位置实参,且后面的关键词实参对应的形参不可以与位置实参冲突
- 默认参数必须放在必选参数的最末尾
- 默认参数不可以赋为可变类型对象
- 分类
- 可变参数
可以接收任意数量的实参,分为可变位置参数与可变关键词参数
*分类
* 可变位置参数
可以接收任意数量的实参,所有参数会依次打包成一个元组
* 可变关键词参数
可以接收任意数量的关键词形式的实参(key
=value
),所有参数会依次打包成一个字典- 备注
- 通过解包序列类型对象的方式(
*list
*tuple
)给可变位置参数赋值,同理也可以用解包字典对象的方式(**dict
)给可变关键词参数赋值 - 通过解包
dict
对象的方式给关键词参数赋值,传递的是深拷贝,函数内部修改并不会影响dict
对象
- 通过解包序列类型对象的方式(
- 备注
- 限定参数
限制了形参赋值方式的参数,分为仅限位置参数与仅限关键词参数
*分类
* 仅限位置参数
对应实参只能通过位置关系传参,通过插入一个/
分隔符来将符号之前的形参设为仅限位置参数
* 仅限关键词参数
对应实参只能通过关键词传参,通过插入一个*
分隔符来将符号之前的形参设为仅限位置参数- 备注
- 当仅限关键词参数前面有一个可变位置参数时,可以不用加
*
分隔符,因为如果不强制使用关键词会被识别为可变位置参数的一部分,导致异常
- 当仅限关键词参数前面有一个可变位置参数时,可以不用加
- 备注
- 混合使用
以上几种形参类型均可混合使用,但定义时必须必须遵循以下顺序:
仅限位置参数
-必选参数
-默认参数
-可变位置参数
-仅限关键词参数
-可变关键词参数
- 备注
- 其中前四种参数:
仅限位置参数
-必选参数
-默认参数
-可变位置参数
都可以使用位置参数 而后两种:仅限关键词参数
-可变关键词参数
必须使用关键词参数 所以任何函数被调用时,无论函数是如何定义的都可以写成func(*arg, **kw)
形式*arg
实参和**kw
实参分别给前四种形参和后两种形参赋值 - 默认参数后接可变位置参数,其默认值是无效的,因为若默认参数省略则会把可变位置实参中的第一个元素识别为默认参数
- 必选参数如果后接可变位置参数,则不可以用关键词传参,因为违反了关键词参数必须在位置参数的后面的规则
- 其中前四种参数:
- 备注
2020-03-02
- 解释
当出于内存等方面的考虑不希望一次性将list
、dict
和str
等对象完整算出,只用在需要的时候给出下一个元素即可的时候。就可以将序列元素替换成对应的生成器(generator
) - 生成方法
- 定义
返回一个生成器迭代器的表达式 - 格式
(expression [if …… else expression] for …… in …… [if ……])
- 定义
- 定义
返回一个generator iterator
的函数。它看起来很像普通函数,不同点在于其包含yield
表达式以便产生一系列值供给for
循环使用或是通过next()
函数逐一获取 - 备注
generator iterator
是generator
函数所创建的对象。每个yield
会临时暂停处理,记住当前位置执行状态 (包括局部变量和挂起的try
语句),而非像return
一样直接中断。当该生成器迭代器恢复时,它会从离开位置继续执行(这与每次调用都从新开始的普通函数差别很大)yield
语句在语义上等同于yield
表达式。yield
语句可用来省略在使用等效的yield
表达式语句时所必须的圆括号。yield
表达式和语句仅在定义generator
函数时使用,并且仅被用于生成器函数的函数体内部。 在函数定义中使用yield
就足以使得该定义创建的是生成器函数而非普通函数
- 定义
- 获取元素方法
- 定义
list
、tuple
、dict
、set
、str
、generator
等可以直接作用于for
循环的对象统称为可迭代对象(Iterable
) - 备注
- 可以使用
isinstance()
(需引入collections
)判断一个对象是否是Iterable
对象
- 可以使用
- 定义
- 定义
可以被next()
函数调用并不断返回下一个值的对象(如generator
)称为迭代器Iterator
- 备注
- 可以使用
isinstance()
(需引入collections
)判断一个对象是否是Iterator
对象 Iterable
对象可以通过iter()
函数转为Iterator
对象for
循环和next()
对象是可以等价转换的Iterator
对象表示的是一个数据流,Iterator
对象可以被next()
函数调用并不断返回下一个数据, 直到没有数据时抛出StopIteration
错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度, 只能不断通过next()
函数实现按需计算下一个数据,所以Iterator
的计算是惰性的,只有在需要返回下一个数据时它才会计算
Iterator
甚至可以表示一个无限大的数据流,例如全体自然数。而使用list
等类型是永远不可能存储全体自然数的
- 可以使用
- 定义
2020-03-06
- 高阶函数
- 定义
接受函数为参数的函数,即为高阶函数(Higher-order function
) - 变量指向函数
将函数赋值给变量,变量便引用函数了。函数名实质上也等同于指向函数的一个变量名.所以函数名也是可以被赋予其他值的(强烈不建议这么做),之后便不再能用该函数名调用函数了 想在所有模块中都让某函数的指向改变,必须在使用function_abs = abs # 将函数赋值给变量,变量便引用函数了 print(function_abs(-1)) # 变量现在可以使用函数的功能了
import
统一修改,格式为import 函数所在模块; 函数所在模块.函数 = 自定义值
import builtins; builtins.abs = 10 # 修改abs变量的指向在其它模块也生效 abs = 10 # 将abs函数名赋值为10
- 与高阶函数相关的函数和方法
- 格式
map(function, iterable, ...) - 参数
- function
map()
调用的函数 - iterable
为function
提供实参的iterable
对象
- function
- 作用
返回一个将function
应用于iterable
中每一项并输出其结果的迭代器 - 备注
- 如果传入了额外的
iterable
参数,function
必须接受相同个数的实参并被应用于从所有可迭代对象中并行获取的项 - 当有多个可迭代对象时,最短的可迭代对象耗尽则整个迭代就将结束
- 如果传入了额外的
- 格式
- 格式
将两个参数的function
从左至右积累地应用到iterable
的条目,以便将该可迭代对象缩减为单一的值。 如果存在可选项initializer
,它会被放在参与计算的可迭代对象的条目之前,并在可迭代对象为空时作为默认值。 如果没有给出initializer
并且iterable
仅包含一个条目,则将返回第一项reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) # 是计算 ((((1+2)+3)+4)+5) 的值 # 左边的参数 x 是积累值而右边的参数 y 则是来自iterable的更新值。
- 参数
- function
map()
调用的函数 - iterable
为function
提供实参的iterable
对象 - initializer
首个实参的默认值
- function
- 格式
- 格式
filter(function, iterable) - 参数
- function
用于提供筛选标准的函数,返回值会被转为True
/False
- iterable
提供待筛选对象的iterable
- function
- 作用
用iterable
中函数function
返回真的那些元素,构建一个新的迭代器。iterable
可以是一个序列, 一个支持迭代的容器,或一个迭代器。
如果function
是None
,则会假设它是一个身份函数,即直接用iterable
中的元素值作为判断标准, 所有返回假的元素(比如0
、False
、None
、[]
等)会被移除 - 备注
filter(function, iterable)
相当于一个生成器表达式:
# function不是None的时候 (item for item in iterable if function(item)) # function是None的时候 (item for item in iterable if item)
- 格式
- 格式
sorted(iterable, *, key=None, reverse=False) - 参数
- iterable
提供待排序的对象 - key
通过引用一个函数来指定排序方法,即将iterable
对象迭代进key
函数中,然后对key
的返回值进行排序
为仅限关键字变量,默认为None
(直接比较对象) - reverse
指定排序的顺序,True
的话则进行逆序处理,为仅限关键字变量,默认为False
- iterable
- 备注
- 内置的
sorted()
确保是稳定的。如果一个排序确保不会改变比较结果相等的元素的相对顺序就称其为稳定的, 这有利于进行多重排序(例如先按部门、再按薪级排序)
- 内置的
- 格式
- 定义
2020-03-08
- 返回函数定义
指函数的返回值为一个函数,是高阶函数的一种用法
当高阶函数返回一个内部函数时,这个内部函数可以引用外部高阶函数的参数和局部变量,可以保存下来一并返回,这种结构称之为闭包(Closure
)
因为闭包结构一般不会自动执行,所以会产生延迟绑定现象。具体来说就是返回函数中的所有变量都是以表达式的形式存在,没有与对象绑定。 只有当函数被调用的时候才会开始将变量与对象绑定。- 延迟绑定带来的问题
当变量为循环变量时延迟绑定就会出现问题:在返回函数的过程中高阶函数的循环语句已经在执行了,等到调用函数时循环语句已经结束了, 所以循环变量只能绑定到循环语句中取到的最后一个值,示例代码如下:def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) # 注意append()的是i*i,而非1*1/2*2/3*3 return fs f1, f2, f3 = count() # fs的值为[f, f, f],因为尚未被调用,所以三个f的返回值皆为i*i print(f1(), f2(), f3()) # 调用f了,f内部各个变量开始绑定对象,而i此时为3,所以f1(), f2(), f3()皆为9(3*3)
- 含循环语句的解决办法
切记返回闭包结构是不要包含循环变量或后续会变化的变量,很容易无法返回正常值。如果内部函数必须要包含循环变量,则可以在高阶函数内部再定义一个函数 用于接收循环变量的值,将其转变为这个函数的局部变量,之后就不会受循环变量的影响了,参考下面代码:def count(): def f(j): # 定义一个用于接收循环变量i的函数 def g(): return j*j # j已经是f的内部变量了,不受i变化的影响 return g # 返回函数彼此独立,所以后面调用f时不会覆盖j值 fs = [] for i in range(1, 4): fs.append(f(i)) # 调用f(i),内部代码立刻被执行,因此i的当前值被传入f() return fs
- 延迟绑定带来的问题
- 备注
- 高阶函数返回函数时,返回的不是函数的值,而是函数本身。因此这个函数是处于未执行状态,需要手动调用
def return_func(): # 定义高阶函数 def sec_func(): # 定义内部函数 pass return sec_func # 返回内部函数 my_func = return_func() # 将return_func()的返回值,即sec_func()函数赋值给变量my_func # 但此时sec_func()并未被调用,所以不会执行sec_func()函数体 my_func() # 这里才是调用了sec_func(),开始执行sec_func()函数体
- 多次调用高阶返回函数,每个函数都是独立的,彼此互不影响.相当于返回内部函数的深拷贝
- 闭包内的变量查找遵循LEGB规则
2020-03-08
- 含义
Python
中也可以不用def
关键字显性地定义函数,而改用匿名函数。在Python
中,通过lambda
关键字对匿名函数进行了有限支持 - 含义
lambda
表达式(有时称为lambda
构型)被用于创建匿名函数。表达式会产生一个函数对象 - 格式
lambda *parameters: expression - 参数
- parameters
lambda
的形参,可以接收多个参数 - expression
函数返回的表达式,一个lambda
函数只能有一个表达式
- parameters
- 备注
lambda *parameters: expression
等价于以下代码:
def <lambda>(parameters): return expression
lambda
函数同样可以赋值或作为高阶函数的参数或返回值- 高阶函数如果想返回
lambda
函数的返回值,可以表示为return (lambda *parameters: expression)()
- 含义
2020-03-11
- 定义
装饰器(decorator
)是指返回值为另一个函数的函数,通常使用@wrapper
语法形式来进行函数变换。可以在不修改原函数的情况下,为原函数修补功能 - 格式
- 自身不带参数的装饰器
上文
def decorator(func): # 装饰器参数接收层 def wrapper(*args, **kwargs): # 目标函数参数接收层 装饰器的执行语句 # 函数执行体 return func(*args, **kwargs) # 返回所接受的函数 @decorator # 将目标函数输入装饰器的语法糖 def myfunc(): return
@decorator
的语法糖等价于:decorator(myfunc)
- 自身带参数的装饰器
上文
def decorator(dcrt_para): # 装饰器参数接收层 def sub_decorator(func): # 函数接收层 def wrapper(*args, **kwargs): # 目标函数参数接收层 装饰器的执行语句 # 函数执行体 return func(*args, **kwargs) # 返回所接受的函数 def wrapper def sub_decorator @decorator(mypara) # 将目标函数输入装饰器的语法糖,并输入装饰器参数 def myfunc(): return
@decorator
的语法糖等价于:decorator(mypara)(myfunc)
- 自身不带参数的装饰器
- 备注
- 使用装饰器会把原函数的__name__属性也修改为
decorator
中的第二层函数的属性,这会导致后续无法正确使用该属性。 所以应当使用functools
模块中的wraps
方法将该属性修正回来。@function.wraps()
语句应放置在接收目标函数的那一层中:
def decorator(func): # 装饰器参数接收层 @function.wraps(func) # 修正函数__name__属性 def wrapper(*args, **kwargs): # 目标函数参数接收层 装饰器的执行语句 # 函数执行体 return func(*args, **kwargs) # 返回所接受的函数
- 使用装饰器会把原函数的__name__属性也修改为
Python
中的偏函数(Partial function
)功能主要是通过functools.partial()
实现的- 格式
functools.partial(func, /, *args, **keywords) - 参数
- func
func
是一个可调用对象或函数。对partial
对象的调用将被转发给func
并附带新的参数和关键字 - args
最左边的位置参数将放置在提供给partial
对象调用的位置参数之前 - keywords
当调用partial
对象时将要提供的关键字参数
- func
- 定义
返回一个新的partial
对象,当被调用时其行为类似于func
附带位置参数args
和关键字参数keywords
被调用。 如果为调用提供了更多的参数,它们会被附加到args
。如果提供了额外的关键字参数,它们会扩展并重载keywords
partial()
会被“冻结了”一部分函数参数和/
或关键字的部分函数应用所使用,从而得到一个具有简化签名的新对象 - 备注
partial()
中*args
,**keywords
的赋值要遵循形参的顺序规则,比如对func(a, b, c)
使用partial(func, b=1)
, 则形参c必须使用关键词赋值(因为位置参数不可以在关键词参数后面)partial
对象与function
对象的类似之处在于它们都是可调用、可弱引用的对象并可拥有属性。但两者也存在一些重要的区别。 例如前者不会自动创建__name__
和__doc__
属性。而且,在类中定义的partial
对象的行为类似于静态方法, 并且不会在实例属性查找期间转换为绑定方法
- 格式
2020-03-15
在代码量很大的时候,处于可维护性等方面的考虑,需要将代码分类放置。这样的组织结构可以使得每个文件内的代码不会过多。
在Python
中,是用后缀名为.py
的文件作为分类存放代码的模块(Module
), 通过导入(import
)的方法将代码串联起来,提高代码的可维护性与复用性。 此外模块式的组织结构还可以避免同一命名空间内来自不同模块的同名属性(attribute
函数、类等)冲突, 可以通过各属性的模块前缀加以区别,例子如下:
import numpy
import math
import scipy
print(math.pi, 'from the math module')
print(numpy.pi, 'from the numpy package')
print(scipy.pi, 'from the scipy package')
* 备注
>1. 自定义的`attribute name`尽量不要与`python`内置`attribute name`重复
>2. 自定义`attribute name`要遵循`python`命名规范
module
也可能会出现命名冲突的问题,因此需要借助包(package
)来实现对module的管理。
具体来说就是将命名冲突的module放进不同的package当中。一个包的目录结构如下:于是mypackage ├─ __init__.py ├─ mymodule1.py └─ mymodule2.py
mymodule1.py
的模块名变为了mypackage.mymodule1.py
。 通过mypackage.
前缀与同名模块进行区分package
也可以采用多级结构,可以在下面继续嵌套子包, 下面的web
就是mymodule
的子包:总的来说引入mycompany ├─ web │ ├─ __init__.py │ ├─ utils.py │ └─ www.py ├─ __init__.py ├─ abc.py └─ utils.py
package
之后,只要顶层package name
不重名,那就不会发生重名问题- 备注
- 自定义的
module name
尽量不要与module name
内置属性名重复 - 自定义
module name
要遵循python
命名规范
- 自定义的
- 备注
- init.py
- init.py的命名方式
__init__.py
的模块名是它所在的package
目录名,而非它自身的目录名+文件名。 在mycompany
这个package
中__init__.py
的module name
不是mycompany.__init__.py
而是mycompany
。同理web
中__init__.py
的module name
不是mycompany.web.__init__.py
而是mycompany.web
- init.py的作用
- 区别包与普通文件夹
__init__.py
是区别package
与普通文件夹的重要因素。 即只有内含了一个名为__init__.py
文件的文件夹, 才能被python
识别为package
- 批量导入包内模块
__init__.py
内容可以为空,但也可以用来批量导入package
内的module
,- 批量导入方法
- 提前import进同级的__init__.py
将本package
中的module
先import
进同级的__init__.py
, 然后只需import
该__init__.py
就可以将本package
中的module
全部引入 - 通配符法
可以将package
中的所有module
以list
形式赋值给·init.py·中的·all·属性。module name
必须作为list
的str
格式元素。 然后使用from package[.subpackage] import *
方式一次性引入所有module
- 提前import进同级的__init__.py
- 批量导入方法
- 区别包与普通文件夹
- init.py的命名方式
import
主要用来导入package
/module
/attribute
,可以拓展为from import as
结构- 具体句型
- import package[.subpackage].module
对于使用import package[.subpackage].module
方式引入的module
调用时必须按照package[.subpackage].module.attribute
的格式 整写出module
与attribute
的名字 - from package[.subpackage] import module
对于使用from package[.subpackage] import module
方式引入module
调用时只需写module.attribute
即可,但这种方式遇到来自不同package
的同名module
时会触发命名空间冲突 - from package[.subpackage].module import attribute
对于使用from package[.subpackage].module import attribute
方式引入module
调用时可以直接用attribute
调用,但是当来自不同module
的attribute
同名时也会触发命名空间冲突 - from package[.subpackage].module import attribute as alias
给引入的attribute
起一个别名,然后就可以以这个别名来调用attribute
其他规则和from package[.subpackage].module import attribute
类似 - from package[.subpackage] import module as alias
给引入的module
起一个别名,然后就可以以这个别名来调用module
其他规则和from package[.subpackage] import module
类似
- import package[.subpackage].module
- 备注
- 当
import
语句包含多个对象(由逗号分隔)时,每个对象将被分别执行, 如同这些对象被分成独立的import
语句一样 import
语句支持使用相对路径导入某一module
或package
的上级package
, 具体方法是在module
或package
前面加前缀.
,没加一个代表回溯一级
# 对于package1.package2.module而言: import .module == import package2 import ..module == import package3
- 当
- 具体句型
大多数Python
代码都遵循这样一个约定:带有下划线前缀的变量应该被当作是API
的私有(private
)部分(无论它是函数、方法或是数据成员)。
但是Python
中其实没有任何机制可以确保私有变量可以不被修改,更多的是依仗与约定俗成的规则
Python
中存在两种私有变量:
* 一个下划线前缀的私有变量
这种私有变量是可以被正常访问与修改,但我们最好当作它们是不可以被修改的
* 两个下划线前缀的私有变量
在class
中,还有一双下划线前缀的私有变量。这种变量不可以被直接访问或修改。原因是系统将其名称隐性地从__varname
改为_classname__varname
可以通过_classname__var
访问与修改该变量,但是及其不推荐这么做,因为不同编译器对这种变量的改名方式可能不一致,可能会导致异常- 备注
- 需要注意的是,仅限从一个对象内部访问的“私有”实例变量在
Python
中并不存在。即Python
没有任何机制可以保证私有变量无法被外部掉用
- 需要注意的是,仅限从一个对象内部访问的“私有”实例变量在
- 备注
2020-03-16
面向过程的程序设计(Procedure-Oriented Programming
,简称为POP
)把计算机程序视为一系列的命令集合,即一组函数的顺序执行。
为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度
面向对象编程(Object Oriented Programming
,简称OOP
),是一种程序设计**。OOP把对象作为程序的基本单元,
一个对象包含了数据和操作数据的函数。面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,
并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递
在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class
)的概念。 类提供了一种组合数据和功能的方法。创建一个新类意味着创建一个新的对象 类型,从而允许创建一个该类型的新实例(Instance
)。 每个类的实例可以拥有保存自己状态的属性(attribute
)。 一个类的实例也可以有改变自己状态的(定义在类中的)方法(method
)- 定义类
使用class关键词定义class
,紧跟class
的是类名ClassName
,按规范需要首字母大写.后面()
内的是基类名BaseClassName
, 表明定义的这个class
由哪个class
派生/继承下来的,实例如下:class Stu(object): """A simple example class""" i = 12345 def f(self): return 'hello world'
- 备注
- 如果没有合适的基类,就使用
object
类,这是所有class
最终都会继承的class
- 如果没有合适的基类,就使用
- 备注
- 类对象操作
class object
的操作分为属性引用与实例化两部分- 属性引用
class object
的attribute
引用使用Python
中所有attribute
引用所使用的标准语法obj.name
。 比如__doc__
也是Stu class
的一个有效的属性,将返回所属类的文档字符串:"A simple example class"
有效的attribute name
是类对象被创建时存在于类命名空间中的所有名称,比上面示例的Stu class
中变量i
和f
都是class attribute
class attribute
也可以被赋值,因此可以通过赋值来更改class attribute
的值 - 实例化
class object
实例化使用函数表示法。可以把class object
视为是返回该class
的一个新instance
的不带参数的函数
- 属性引用
- 定义类
调用class object
返回的对象就是实例对象(instance object
),instance
能保留class
的所有数据,包括可以拥有保存自己状态的attribute
, 以及可以有改变自己状态的(定义在类中的)method
- 创建实例对象
创建instance
是通过class name
+()
实现的,比如lisa = Stu('Lisa', 79)
就是将Stu class
的instance
赋值给变量bart
- 实例属性绑定
可以自由地给一个instance
对象绑定attribute
,比如给instance_object1
绑定一个class object
没定义过的attribute1
和静态语言不同,Python
允许对instance
对象绑定任何数据。也就是说,对于两个instance变量, 虽然它们都是同一个class
的不同instance
,但拥有的attrribute
都可能不同。比如instance_object2
就没有attribute1
instance_object1 = Stu() instance_object2 = Stu() instance_object1.attribute1 = attribute1
- 实例对象操作(属性调用)
instance object
唯一的操作就是属性调用,可以调用的实例属性分为数据属性与实例方法两种- 实例属性
- 数据属性
数据属性不需要声明。像局部变量一样,它们将在第一次被赋值时产生。 - 实例方法 详见方法对象部分
- 数据属性
- 实例属性
- 数据封装
class
的instance
可以保留类的所有数据,想要访问这些内部数据没必要专门在外部设计函数来访问它们。可以直接在class
的内部定义调用这些数据的函数, 就可以尽可能地将这些数据限制在class
内部,这称之为数据封装。对数据和逻辑封装的好处在于调用很容易,但却不用知道内部实现的细节, 可以当个黑盒使用,从而避免了外部对class
内部数据的过度依赖
- 创建实例对象
这些封装数据的函数是和class
本身是关联起来的,我们称这些class
的函数为instance
的method
,或者说method
就是与instance
绑定的函数- 定义方法
要定义一个method
,除了第一个形参必须是self
外,其他(如形参的规则)和普通函数的定义方法一样 - 调用方法
要调用一个method
,只需要在instance
变量上直接调用,格式为instance.method
除了self
不用传递,其他参数正常传入
instance
的attribute
在method
内可以通过self
参数调用def print_score(self): print('{:s}\'s score is {:d}'.format(self.name, self.score)) instance_object1 = Stu() instance_object1.print_score()
- __init__方法
为了发挥class
的模板作用,可以在class
内部定义一个特殊的__init__ method
,强制在instance
创建时就必须填入指定的参数def __init__(self, name, score): self.name = name # 意为将形参name接收到的对象赋值给instance(self)的name attribute self.score = score # 意为将形参score接收到的对象赋值给instance(self)的score attribute
- 备注
method
类似闭包会产生延迟执行等性质
- 定义方法
- 备注
- class与instance的关系:
class
是一种抽象概念,比如我们定义的class
——Student
,是指学生这个概念, 而实例(instance
)则是一个个具体的Student
,比如,Bart Simpson
和Lisa Simpson
是两个具体的Student
。 所以,面向对象的设计**是抽象出Class
,根据Class
创建Instance
- instance与method的关系
method
是指与对应instance
绑定了的函数。即instance.f
是实例的数据属性还是方法属性,取决于是否存在class.f
这个函数, 需要注意的是instance.f
与instance
绑定了所以就是方法对象,而class.f
是函数对像
- class与instance的关系:
- 定义
- 子类
在面向对象程序设计中,当我们定义一个class
的时候,可以从某个现有的class
继承,新的class
称为子类(subclass
), 而被继承的class
称为基类、父类或超类(Base class
、Super class
) - 基类
subclass
可以获得父类的全部功能,可以在原有功能功能基础上修改基类的method
(本质上是替换了method name
的指代对象,不会影响基类的同名method
)subclass
也可以添加基类没有定义的method
,同样不会影响基类
- 子类
- 类的数据类型
可以使用isinstance()
函数判断一个对象是否为指定的类型- 子类
对于subclass
,除了可以被识别为本身的类型之外,还可以被识别为他的任意基类类型 - 基类
但对于基类来说,则无法被识别为它的subclass
- 子类
- 备注
- 基类名称
BaseClassName
必须定义于包含派生类定义的作用域中。也允许用其他任意表达式代替基类名称所在的位置。 - 如果请求的
attribute
在class
中找不到,搜索将转往基类中进行查找。如果基类本身也派生自其他某个class
,则此规则将被递归地应用 - 在派生类中的重载方法中想要实现扩展而非简单地替换同名的基类方法。有一种方式可以简单地直接调用基类方法:
即调用
BaseClassName.methodname(self, arguments)
。有时这对客户端来说也是有用的。 (请注意仅当此基类可在全局作用域中以BaseClassName
的名称被访问时方可使用此方式。)
- 基类名称
- 定义
- 定义
多态就是将class
通过派生出不同的subclass
实现同一个基类的method
在不同subclass
上呈现不同形态的结果 - 多态的优点
针对这一特性,当我们想设计对所有subclass
适用的method
时,只需考虑基类的method
适配情况,而不用逐个对subclass进行适配, 因为基类的操作会对其所有subclass
兼容。后续基类继续产生新subclass
时,只要确保method
编写正确,不用管原来的代码是如何调用的, 这些method
仍会适用于新subclass
。
因此对于一个变量,我们只需要知道它是属于哪个class
,无需确切地知道它是哪个subclass
,只要method
编写正确,就可以放心地调用 这样调用方只管调用,不用管细节。同时因为不同subclass
间的同名method
作用不同,所以可以实现一个接口不同效果 - 开闭原则
开闭原则是面向对象编程的重要原则之一,即*对扩展开放,对修改封闭
*- 对扩展开放
class
对象增加新subclass
不会对基于多态的函数造成影响,可以有效兼容 - 对修改封闭
新增了subclass
不需要对class
的对应method
进行修改
- 对扩展开放
- 鸭子类型
指动态类型语言的一种编程风格,它并不依靠查找对象类型来确定其是否具有正确的接口,而是查看对象是否有对应的目标方法或属性(名称符合即可) ("看起来像鸭子,叫起来也像鸭子,那么肯定就是鸭子")
由于强调接口而非特定类型,设计良好的代码可通过允许多态替代来提升灵活性。鸭子类型避免使用type()
或isinstance()
检测。 (但要注意鸭子类型可以使用抽象基类作为补充)
- 定义