python入门细节
相除后的类型
1
2
3
4
|
type ( 2 / 2 ) float type ( 2 / / 2 ) int |
双斜杠是整除,出来的类型是int。单斜杠的出来的是float类型。
进制表示和转换
进制表示:
二进制:0b 八进制:0o 十六进制:0x
进制转换:
转换为二进制:bin() 转换为八进制:oct() 转换为十进制:int() 转换为十六进制:hex() 转换为布尔类型:bool()
布尔类型
布尔类型转换:bool()
布尔类型属于数字这个基本数据类型里面只要是非零的数字,bool类型的值为True 对于字符串,布尔类型为True,除了空字符串 bool值为False:
bool(0) bool('') 中间没有空格 bool([]) bool({}) bool(None) bool(NoneType) bool(set{})
多行字符串
三个引号可以再IDLE下回车不会执行换行。print函数可以输出n这样的反义字符。
单个引号想回车换行可以再前面加上字符即可。
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
|
''' sadasdj adas ''' Out[ 1 ]: '\nsadasdj\nadas\n' 'asda File "<ipython-input-2-6af9d7d5e65d>" , line 1 'asda ^ SyntaxError: EOL while scanning string literal 'asdd\ adsad\ sad' Out[ 3 ]: 'asddadsadsad' print ( 'asda\nsada\n' ) asda sada ''' asda\n ''' Out[ 5 ]: '\nasda\n\n' |
原始字符串
原始字符串在print时只是输出里面的字符,不考虑反义之类的,小心r大写R都没有关系。
1
2
3
4
5
6
7
8
|
print (r 'c:\nsda\nsds' ) c:\nsda\nsds print (r 'let' s go') File "<ipython-input-3-a81b31c0c433>" , line 1 print (r 'let' s go') ^ SyntaxError: invalid syntax |
字符串的运算
1.字符串的'+'和'*'
1
2
3
4
5
|
"hello" + "world" Out[ 1 ]: 'helloworld' "hello" * 3 Out[ 2 ]: 'hellohellohello' |
2.获取字符串里的字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
"hello world" [ 0 ] Out[ 3 ]: 'h' "hello world" [ - 1 ] Out[ 4 ]: 'd' # 包括左面但是不包括右面 "hello world" [ 0 : 4 ] Out[ 5 ]: 'hell' "hello world" [ 0 : - 1 ] Out[ 6 ]: 'hello worl' # 超出长度时会按字符串最大的长度进行截取 "hello world" [ 0 : 20 ] Out[ 7 ]: 'hello world' # 没有右边的值的时候,表示直接输出到末尾 "hello world" [ 6 :] Out[ 8 ]: 'world' # 负数在冒号前面的时候 "hello world" [ - 4 :] Out[ 9 ]: 'orld' |
python表示序列的方式 1.列表(list) 列表中的元素可以是任意类型的组合,比如列表的嵌套,布尔类型,字符串等等。
1.1 基本操作
1.1.1 基本选取(切片)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
[ "新月打击" , "苍白之瀑" , "月之降临" , "月神冲刺" ] Out[ 10 ]: [ '新月打击' , '苍白之瀑' , '月之降临' , '月神冲刺' ] [ "新月打击" , "苍白之瀑" , "月之降临" , "月神冲刺" ][ 0 ] Out[ 11 ]: '新月打击' [ "新月打击" , "苍白之瀑" , "月之降临" , "月神冲刺" ][ 0 : 2 ] Out[ 12 ]: [ '新月打击' , '苍白之瀑' ] [ "新月打击" , "苍白之瀑" , "月之降临" , "月神冲刺" ][ - 1 :] Out[ 13 ]: [ '月神冲刺' ] a = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ] print (a[ 0 : 3 ]) print (a[ 0 : len (a): 2 ]) print (a[ len (a): 0 : - 2 ]) [ 1 , 2 , 3 ] [ 1 , 3 , 5 , 7 ] [ 8 , 6 , 4 , 2 ] #可以看到,切片操作很简单,第二个冒号后面的数字可以看作是步长。注意负数时的用法。 |
可以看到,当没有冒号的时候,单个选取出的是元素的类型。但是当有冒号的时候,选取出的是序列的类型。这里需要注意
1.1.2 列表的相加和乘法
1
2
3
4
5
|
[ "新月打击" , "苍白之瀑" , "月之降临" , "月神冲刺" ] + [ '虚弱' , '点燃' ] Out[ 14 ]: [ '新月打击' , '苍白之瀑' , '月之降临' , '月神冲刺' , '虚弱' , '点燃' ] [ '虚弱' , '点燃' ] * 3 Out[ 15 ]: [ '虚弱' , '点燃' , '虚弱' , '点燃' , '虚弱' , '点燃' ] |
1.1.3 判断元素是否存在
运用in和not in即可
1
2
3
4
5
|
3 in [ 1 , 2 , 3 , 4 ] Out[ 21 ]: True 3 not in [ 1 , 2 , 3 , 4 ] Out[ 22 ]: False |
1.1.4 计算长度,最大小值
1
2
3
4
5
6
7
8
9
10
11
|
len ([ 1 , 2 , 3 ]) Out[ 23 ]: 3 len ( "hesadad" ) Out[ 24 ]: 7 max ([ 1 , 2 , 3 , 4 , 5 , 6 ]) Out[ 25 ]: 6 min ([ 1 , 2 , 3 , 4 ]) Out[ 26 ]: 1 |
1.1.5 append()
可以向列表中追加元素。
1
2
3
4
5
|
a = [ 1 , 2 , 3 , 4 ] a.append( '5' ) Out[ 22 ]: [ 1 , 2 , 3 , 4 , '5' ] |
2.元组(tuple) 元组的操作,包括访问,加,乘,in等操作和列表是相同的。需要注意一点是:
1
2
3
4
5
6
7
8
|
type (( 1 )) Out[ 16 ]: int type (( 'sd' )) Out[ 17 ]: str type (( 1 , 2 , 3 )) Out[ 18 ]: tuple |
如果括号里有一个元素,默认为是一个运算,不会认为是元组的括号。如果要定义只有一个元素的元组的:
1
2
3
4
5
|
type (( 1 ,)) Out[ 19 ]: tuple # 括号里面什么都没有表示一个空元组 type (()) Out[ 20 ]: tuple |
元组是序列,不可变类型,但是如果元组里包含了列表,比如:
1
2
3
4
5
6
|
a = ( 1 , 2 , 3 ,[ 4 , 5 ]) a[ 3 ][ 1 ] = '2' print (a) ( 1 , 2 , 3 , [ 4 , '2' ]) |
我们可以看到元组里的列表可以改变
3.字符串(str)
字符串和元组都是不可变的类型序列包括了字符串,列表和元组,序列都可以用下标索引和切片的方式。
set集合 set集合里的元素是无序的,不重复的。 in,not in,len,max,min,但是没有加,乘这种操作。集合有相减,交集,并集等操作
1
2
3
4
5
6
7
8
|
{ 1 , 2 , 3 , 4 , 5 , 6 } - { 1 , 2 } Out[ 1 ]: { 3 , 4 , 5 , 6 } { 1 , 2 , 3 , 4 , 5 , 6 } & { 1 , 2 } Out[ 2 ]: { 1 , 2 } { 1 , 2 , 3 , 4 , 5 , 6 } | { 1 , 2 , 7 } Out[ 3 ]: { 1 , 2 , 3 , 4 , 5 , 6 , 7 } |
定义一个空集合的方法:set()
1
2
3
4
5
6
7
8
|
type ({}) Out[ 8 ]: dict type ( set ()) Out[ 9 ]: set len ( set ()) Out[ 10 ]: 0 |
字典(dict) 字典和集合类型(set)有些类似,里面是无序的,所以字典不是序列。字典中可以value可以使任意类型;但是key是可以的,key必须是不可变的类型,比如Int,str,tuple等,例如list就是不可以的。字典的访问:{'key1':'value1,'key2':'value2'}['key1'],字典的访问通过key来进行字典里,key值是不可以重复的,如果定义有重复虽然不会报错,但是会自动选择其中一个。
序列,集合和字典属于组,是Python的基本数据类型。
变量 变量的定义时,首字母不能是数字,但可以是下划线。字母,数组,下划线可以组成变量。 Python 变量名区分大小写。定义变量的时候不用指明类型,和C++不一样。 值类型和引用类型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
a = 1 b = a a = 3 print (b) 1 a = [ 1 , 2 , 3 , 4 ] b = a a[ 0 ] = '1' print (b) [ '1' , 2 , 3 , 4 ] |
值类型:int str tuple(不可改变),在重新定义的时候,因为不可改变会生成一个新的对象,这个时候b仍然指向原对象,a指向了一个新对象
引用类型:list,set,dict(可以改变),在定义一个新对象的时候,会在原对象的基础上进行改变而不产生新对象,所以无论是a还是b都会指向已经改变的原对象,所以a和b的值都会变化。
再进一步的,可以看以下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
a = 'hello' id (a) Out[ 15 ]: 1510080259608 a = a + 's' id (a) Out[ 17 ]: 1510081716832 a[ 0 ] = 's' Traceback (most recent call last): File "<ipython-input-18-02436d67df37>" , line 1 , in <module> a[ 0 ] = 's' TypeError: 'str' object does not support item assignment |
id()是查询在计算机中的内存位置,我们可以看到发生了变化。所以a = a + 's'是可以的。但是对字符串的赋值操作,是不可以的,因为str是不可变的类型。
运算符 python中是没有自增和自减这种操作的。表示“等于”是'==',“不等于”是'!=' 字符串相比较的时候,把字符串中每一个字符拿出来相比较,比较AscII码值比较两个列表,和字符串是相同的。元组也可以进行比较,和列表和字符串是相同的。非bool类型在参与逻辑运算的时候,比如int,float,str等类型,在参与and,or,not的时候,遵循的规则和c++中类似。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
0 and 1 Out[ 1 ]: 0 1 and 2 Out[ 2 ]: 2 1 and 0 Out[ 3 ]: 0 1 or 2 Out[ 4 ]: 1 0 or 1 Out[ 5 ]: 1 |
由上面的例子我们可以看出and和or的逻辑判断规则和c++一致。空字符串,0等判断为空,在上面的笔记中有记载。
成员运算符: in, not in
成员运算符表示一个元素是否在一个组里;成员运算符返回值类型是bool类型。在字典中,是判断key值。
1
2
3
4
5
6
7
8
9
|
a = 1 a in { 1 : '1' } Out[ 7 ]: True a = '1' a in { 1 : '1' } Out[ 9 ]: False |
身份运算符
is, is not
身份运算符比较的是身份而不是值,简单来说就是内存地址。和关系运算符“==”不一样。
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
a = 1 b = 1 a is b Out[ 12 ]: True id (a) Out[ 13 ]: 1438837200 id (b) Out[ 14 ]: 1438837200 b = 1.0 a is b Out[ 16 ]: False id (b) Out[ 17 ]: 2197963106536 a = = b Out[ 18 ]: True a = { 1 , 2 , 3 } b = { 2 , 1 , 3 } a = = b Out[ 21 ]: True a is b Out[ 22 ]: False c = ( 1 , 2 , 3 ) d = ( 2 , 1 , 3 ) c = = d Out[ 25 ]: False c is d Out[ 26 ]: False id (a) Out[ 27 ]: 2197907160424 id (b) Out[ 28 ]: 2197990760232 |
我们可以看到,在无序的set集合中,元素顺序不一样在内存中位置不同,虽然值相同但是身份仍然不一样。
位运算符
&, |, ^, ~, <<, >>
以上都是把数字当作二进制进行运算。把数字按照二进制进行换算,以&举例,相同位1,不同为0。然后再把二进制数转换成数字原来的进制。eg: 2&3 == 2
判断变量的类型
python中一切都是对象,对象有三大特征,值(value), 身份(id), 类型(type)。判断变量的类型,可以使用isinstance()这个函数。
1
2
3
4
5
6
7
8
9
10
|
a = 'sds' isinstance (a, str ) Out[ 30 ]: True isinstance (a,( str , int , float )) Out[ 31 ]: True isinstance (a, int ) Out[ 32 ]: False |
isinstance可以判断对象中的子类是否满足条件,所以比较好。
vscode python 基本操作
+ 单行注释:# 快捷键:ctrl + /
+ 多行注释:""" """ 快捷键:alt + shift + a
pylint 每个文件(模块)需要有开篇的注释来说明作用。 python中不存在常量(constant)一说,但是对于形式上的常量,一般以全部大写来表示 Python变量中两个名字的衔接用下划线,eg:test_account. python包和模块注意事项 包和模块是不会被重复导入的。尽量避免循环引入。导入一个模块的时候,会执行这个模块里面的代码。
python中的普通模块必须有一个包,当想要把一个可执行文件当作一个普通模块运行时,可以使用-m参数,如:
python -m 命名空间.模块名
注意:此处若当作普通模块,必须包括包名/命名空间。python中可执行文件没有所属包。此外,当使用-m参数后,顶级包也相对改变。
dir函数
用来查看模块或者类内部的变量,包括系统内置变量。
1
2
3
4
5
6
7
8
|
import sys infos = dir (sys) print (infos) [ '__displayhook__' , '__doc__' , '__excepthook__' , '__interactivehook__' , '__loader__' , '__name__' , '__package__' , '__spec__' , '__stderr__' , '__stdin__' , '__stdout__' , '_clear_type_cache' , '_current_frames' , '_debugmallocstats' , '_enablelegacywindowsfsencoding' ,......] # 可见打出了许多的变量,是sys模块内部的变量。下面的代码中也有应用,只不过没有参数。 |
__name__的应用技巧
1
2
3
4
|
if __name__ = = '__main__' : pass #来判断模块是否被当作入口文件被调用,如果被当做模块就不会打印if条件成立执行的语句,如果被当做入口文件才会执行 |
1.模块导入的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 由父包引入子包或者同级别引入的情况 import module # 只能引入同一个包(不包括子包)里的模块。注意这里不能直接引入模块的变量。 import module as name # 使用的时候name.变量/函数等。 from packet import module # 可以父包里的子包引入模块。 from packet.module import module.变量 / 函数等 from module import * # 引入module内__all__指定的变量/函数等。 from module import module.变量 1 , module.变量 2 ,...... # 引入多个变量可用逗号隔开 |
2.__init__.py
该文件,可以在导入一个包,或者导入包中的函数的时候,系统会首先执行该文件。
1
|
from packet import * # 这行代码会引入被引入包中__init__.py中__all__指定的模块。 |
3.模块内置变量
1
2
3
4
5
6
7
8
|
a = 2 b = 1 infos = dir () print (infos) [ '__annotations__' , '__builtins__' , '__cached__' , '__doc__' , '__file__' , '__loader__' , '__name__' , '__package__' , '__spec__' , 'a' , 'b' ] # 上方除了'a','b'都是系统内置的变量。 |
下面介绍几个比较重要的内置变量,利用import一个模块的时候会执行该模块里面的内容的机制。对于入口文件和模块文件,内置变量的值有所不同。
模块文件:
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
|
''' this is a c3 doc ''' print ( "name: " + __name__) print ( "package: " + __package__) print ( "doc: " + __doc__) print ( "flie: " + __file__) import sub11.c3 PS D:\pycode\sub1> python c2.py name: sub11.c3 package: sub11 doc: this is a c3 doc flie: D:\pycode\sub1\sub11\c3.py # __doc__记录的是该模块的开头注释 # __name__记录该模块的名字 # __package__记录该模块属于的包 # __file__记录该模块的文件的绝对路径 |
入口文件
如果一个.py文件被当做一个应用程序的入口:
①它的名称不再是本身的模块名称,而是被强制更改为__main__
②它不属于任何包
③file内置变量不会像普通模块一样显示绝对路径,它所显示的值也不是确定值,和执行命令所在目录有关
注:python入口文件和普通导入的模块文件是有差异的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
''' this is a c3 doc ''' print ( "name: " + __name__) print ( "package: " + ( __package__ or "当前模块不属于任何包" )) print ( "doc: " + __doc__) print ( "flie: " + __file__) name: __main__ package: 当前模块不属于任何包 doc: this is a c3 doc flie: c3.py # 该文件属于sub11包(有__init__.py这个文件),但是我们是直接执行的c3.py文件,可见此时如果在c3.py中打印内置变量,__name__被强制定位__main__,而且package上也不会显示出所属于的包,file路径也发生了变化。 |
4.绝对导入和相对导入
绝对导入是指从入口文件引入执行文件所在的文件夹的包中的模块的时候,需要进行绝对导入。
相对导入指从模块引入父级的模块时,需要进行相对导入。
顶级包与入口文件main.py的位置有关,与main.py同级的包就是该包下所有模块的顶级包。而对于入口文件来说不存在包的概念。
绝对导入/绝对路径:从顶级包到被导入模块名称的完整路径。
相对导入,一个'.'表示当前包,两个'..'表示上一级包.'...'上上级包,以此类推
注意:
import不支持相对导入,只能使用from import格式实现相对导入。
入口文件中不能使用相对导入,因为它没有包的概念。
使用相对导入不要超出顶级包///和入口文件同级都不能使用相对导入
函数注意事项 python默认有一个递归次数限制来防止无限递归调用,但可以设置递归最大次数:
1
2
3
|
import sys sys.setrecursionlimit( 10000 ) # 可以设置最大迭代10000次。不过理论上虽然设置这么多,但实际上仍然达不到允许迭代这么多次。 |
python中for循环内定义的变量可以在外部使用,这点和c和java不相同。若函数体中没有返回值,则认为返回None。
1.return 返回多个值,链式赋值和序列解包
python函数返回多个值直接在return后面用逗号分隔要返回的值即可,返回结果是tuple元组类型。比较好的接收函数返回的多个值的方法不是用一个变量接收元组然后用序号访问它的元素,而是直接用多个值接收然后分别使用这些变量,如:
1
2
3
4
5
6
7
|
def damage(skill1, skill2) damage1 = skll1 * 3 damage2 = skill2 * 3 + 10 return damage1, damage2 skill1_damage, skill2_damage = damage( 3 , 6 ) print (skill1_danage, skill2_damage) |
上面的接受参数的方式叫做序列解包。
链式赋值和序列解包
1
2
3
4
5
6
7
8
9
10
11
12
|
d = 1 , 2 , 3 print ( type (d)) a, b, c = d print (a, b, c) print ( '----------------' ) a = b = c = 1 print (a, b, c) < class 'tuple' > 1 2 3 - - - - - - - - - - - - - - - - 1 1 1 |
因为是序列解包,所以可以不是元组,列表也是可以的。最后如果多个变量取同一个值,那么可以用上面的方法来进行赋值。
2.函数参数
函数参数有:
必须参数:形参和实参。关键字参数默认参数可变参数可变参数可以解开可变,并且可以进行可变关键字参数。定义可变参数后,传值的时候可以什么都不传,这个时候是空元组或者空字典。
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
def demo( * param): print (param) print ( type (param)) demo( 1 , 2 , 3 ,[ 4 , 5 , 6 ]) ( 1 , 2 , 3 , [ 4 , 5 , 6 ]) < class 'tuple' > # 传入可变参数,会定义为元组。 def demo( * param): print (param) print ( type (param)) a = 1 , 2 , 3 demo(a) demo( * a) (( 1 , 2 , 3 ),) < class 'tuple' > ( 1 , 2 , 3 ) < class 'tuple' > # *可以解包。 def demo( * * param): print (param) print ( type (param)) demo(q = '万能牌' , w = '切牌' , e = '占卜' ) { 'q' : '万能牌' , 'w' : '切牌' , 'e' : '占卜' } < class 'dict' > # 可见传进来以后是一个字典,很方便。这就是关键字可变参数。 def demo( * * param): print (param) print ( type (param)) for key,value in param.items(): print (key, ':' ,value,end = '|| ' ) demo(q = '万能牌' , w = '切牌' , e = '占卜' ) { 'q' : '万能牌' , 'w' : '切牌' , 'e' : '占卜' } < class 'dict' > q : 万能牌|| w : 切牌|| e : 占卜|| # 传入字典时可以采用上面的方式取出键值和内容。 def demo( * * param): print (param) print ( type (param)) for key,value in param.items(): print (key, ':' ,value,end = '|| ' ) a = { 'q' : '万能牌' , 'w' : '切牌' , 'e' : '占卜' } demo( * * a) { 'q' : '万能牌' , 'w' : '切牌' , 'e' : '占卜' } < class 'dict' > q : 万能牌|| w : 切牌|| e : 占卜|| # 和传入元组一样,解序列可以传入两个*。 |
形参是定义函数的时候定义的参数,实参是调用函数的时候传递的参数。关键字参数通过指定形参来进行参数赋值。可变参数在必须参数之后,默认参数之前,否则会出现赋值的错误
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
|
def demo(param1,param2 = 2 , * param3): print (param1) print (param2) print (param3) demo( 'a' , 1 , 2 , 3 ) a 1 ( 2 , 3 ) # 可见如果默认参数在可变参数之前,会发生错误,和预想的(1,2,3)赋值给param3有区别。 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # 调整一下顺序可以得到想要的结果 def f1(name1, * args, name2 = '2' , * * kw): print (name1) print (name2) print (args) print (kw) f1( '1' , '3' , '4' ,a = '1' ,b = '2' ) 1 2 ( '3' , '4' ) { 'a' : '1' , 'b' : '2' } |
类注意事项 类名最好不要用下划线,有多个单词的时候可以采用大写首字母的方法。类的最基本作用就是封装。定义类,实例化对象类只负责定义和刻画,并不负责去执行代码。所以在类里面去执行方法是不正确的。在一个模块里面,不要既定义类,又去实例化类执行代码。不要把类和模块搞混,类里面有自己的规则 1.构造函数 构造函数即__init__(self)
: 实例化类的时候构造函数被自动执行 构造函数返回值为None ,不能人为return更改。可以通过类名.__init__()
来执行构造函数。 2.类变量和实例变量 类变量是定义在类内但是不在__init__()
中;实例变量是定义在___init__()
中的。换句话说,实例变量是对象的,类变量是类的,二者不能混淆。 python中,类与对象的变量查找是有顺序的。
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
34
35
36
|
class Student(): name = 'Catherian' age = 0 high = 170 def __init__( self , name, age, high): self .name = name self .age = age high = high # 注意这里的身高没有用self.high来定义 def doHomework( self ): print ( 'doHomework' ) student1 = Student( '呵呵哒' , 18 , 180 ) student2 = Student( 'ojbk' , 16 , 175 ) print (student1.name, student1.age, student1.high) print (student2.name, student2.age, student2.high) print (Student.name, Student.age, Student.high) print (student1.__dict__) print (Student.__dict__) 呵呵哒 18 170 ojbk 16 170 Catherian 0 170 # 这里打印出的才是类变量 # 可以看到,尽管我们在实例化student1,student2的时候传入了身高high这个数据,但是打印的时候我们发现输出的是类变量high,并不是实例变量。 { 'name' : '呵呵哒' , 'age' : 18 } # __dict__对与对象,打印出的是对象的实例变量。可见里面并没有high。所以实例变量是用 self. 来定义的。类的__dict__是打印出对象里面的内容,包括数据成员和方法,下面即是 { '__module__' : '__main__' , 'name' : 'Catherian' , 'age' : 0 , 'high' : 170 , '__init__' : <function Student.__init__ at 0x000001F06B70F9D8 >, 'doHomework' : <function Student.doHomework at 0x000001F06B70FA60 >, '__dict__' : <attribute '__dict__' of 'Student' objects>, '__weakref__' : <attribute '__weakref__' of 'Student' objects>, '__doc__' : None } |
虽然我们在实例化对象时传入了数据,但是我们发现high是类变量不是实例变量,但是仍然sudent1.high打印出了变量,这是类变量而不是实例变量。这是因为,python中的查找机制:当查找实例变量不存在的时候,会继续向上查找类中相对应的类变量,如果子类中没有父类中有(出现继承)时,会查找到父类。所以我们打印出了类变量。
3.实例方法 python中,实例方法的参数里必须要显式的定义出self,但在外部调用的时候不需要给出self。self指的就是你在外部实例化的对象。所以self.
给出的是实例变量。 在实例方法中调用实例变量最好用self.
的形式来进行调用。因为你传入的是形参. 在实例方法中调用类变量有两种方法:类名.类变量
和self.__class__.类变量
4.类方法定义类方法:
1
2
3
4
5
6
7
|
class Student sum = 0 @classmethod def student_sum( cls ): pass # cls可以更换,函数上面是一个装饰器,表示了这是一个类方法。cls换成self或者其他任意的都是可以的,和实例方法中self可以任意替换别的字符是一样的。 |
对于类方法,调用类变量:cls.类变量
即可。所以cls代表的就是所属于的类。在调用类方法的时候,可以 Student.studen_sum
也可以通过对象来调用,即:student1.student_sum
。但是建议用类名来调用比较好。
5.静态方法定义静态方法:
1
2
3
4
5
|
class Student sum = 0 @staticmethod def add(x,y) pass |
静态方法上同样要有装饰器来修饰,但是函数中不用显式的传入self或者cls,更像是一个普通的函数。在类方法和静态方法中都不能调用实例变量。一般不建议用静态方法,类方法更方便。
6.成员可见性
在python中,实际上没有什么是不能访问的。成员可见性更像是一种标志,一切全靠自觉。定义私有变量或者方法的时候,只需要在变量或者方法前面加上双下划线就代表了私有(注意不要同时在后面再加上双下划线)。
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
# 我们定义一个学生类 class Student(): name = 'Catherian' age = 0 __high = 170 def __init__( self , name, age): self .name = name self .age = age self .__score = 0 def __doHomework( self ): print ( 'doHomework' ) student1 = Student( '呵呵哒' , 18 ) print (student1.__score) Traceback (most recent call last): File "c1.py" , line 15 , in <module> print (student1.__score) AttributeError: 'Student' object has no attribute '__score' # 可以看到不能这样访问。 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # 如果我们再加上一句再执行: student1 = Student( '呵呵哒' , 18 ) student1.__score = - 1 print (student1.__score) - 1 # 我们发现竟然可以成功赋值并且访问的。这是因为 studen1.__score = -1 这个操作其实是又定义了一个新的实例变量。我们可以打印看一下 student1 = Student( 'okk' , 18 ) student1.__score = - 1 # print(student1.__score) print (student1.__dict__) { 'name' : 'okk' , 'age' : 18 , '_Student__score' : 0 , '__score' : - 1 } # 我们可以看到,我们再里面定义的__score变量被定义成了_Student__score变量,__score变量是我们根据Python动态特性新定义出的实例变量。所以要访问私有变量也很简单: print (student1._Student__score) 0 # 所以这个真的全靠自觉,python中没什么是不能访问的。 |
7.继承性
python中可以单继承也可以多继承。
子类继承父类,会继承父类中的变量(类变量和实例变量都会继承)和父类中的方法。在子类内部,如果有和父类重名的变量,会按照我们在类变量和实例变量
中说明的搜索规则进行。如果有重名的方法,想在子类内部进行调用,可以采用super(子类名, self)
进行调用父类重名函数。在python中,可以用类名调用实例方法。很奇怪但是是可以的。在子类构造函数中,通过传入多个参数,调用父类构造函数即可完成初始化。多继承容易出现BUG。
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
34
35
36
37
38
39
|
# 定义一个Human父类 class Human(): sum = 0 def __init__( self , name, age): self .name = name self .age = age self .__class__. sum + = 1 def get_name( self ): print ( self .name) def do_Homework( self ): # 重名的方法 print ( "this is a parent method" ) # 定义一个子类Student from c2 import Human class Student(Human): sum = 0 # 和父类重名的类变量 def __init__( self , name, age, score): # 父类还有两个参数,所以这里有三个参数 Human.__init__( self , name, age) # 这里注意通过类名调用方法,不能不加self self .score = score self .__class__. sum + = 1 def do_Homework( self ): # 和父类重名的方法。 super (Student, self ).do_Homework() print ( 'doHomework' ) student1 = Student( 'okk' , 18 , 61 ) print (student1. sum ) print (Student. sum ) print (student1.get_name()) print (student1.do_Homework()) 2 # 可见通过父类方法里对sum的操作,继承到子类中时,对于重名变量仍然可以操作子类中的重名变量。 2 # 根据搜索机制,实例变量里没有找到子类里的类变量,再没有找父类。 okk None this is a parent method # 可见调用了父类中的方法。 doHomework None |
枚举
python中的枚举类型其实是一个类。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
from enum import Enum class diamond(Enum): # 必须要继承父类Enum YELLOW = 1 BLUE = 2 GREEN = 3 RED = 4 print (diamond.YELLOW) diamond.YELLOW # 可见打印出的就是diamend.YELLOW |
在枚举类型中,每个类型有不同的值,不允许出现相同类型赋不同值,值可以是任意类型的。如果出现了两个枚举类型的值相同,下面的枚举类型会被当成是上面枚举类型的别名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from enum import Enum class diamond(Enum): YELLOW = 1 BLUE = 1 GREEN = 3 RED = 4 print (diamond.BLUE) print (diamond.__members__.items()) # items()可以不需要。打印出所有的枚举类型。 diamond.YELLOW # 可打印的是BLUE出来的是YEELOW。 odict_items([( 'YELLOW' , <diamond.YELLOW: 1 >), ( 'BLUE' , <diamond.YELLOW: 1 >), ( 'GREEN' , <diamond.GREEN: 3 >), ( 'RED' , <diamond.RED: 4 >)]) |
不能在类的外部修改类型的值,比如diamond.YELLOW = 5
是会报错的。类型最好用大写表示,表示为常量不能修改。枚举类型,枚举名称,枚举的值,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from enum import Enum class diamond(Enum): YELLOW = 1 BLUE = 2 GREEN = 3 RED = 4 print ( "枚举类型为:" , type (diamond.GREEN), diamond.GREEN) print ( "枚举的名称为" , type (diamond.GREEN.name), diamond.GREEN.name) print ( "枚举的值为:" , diamond.GREEN.value) 枚举类型为: <enum 'diamond' > diamond.GREEN 枚举的名称为 < class 'str' > GREEN 枚举的值为: 3 |
可以采用for循环获得枚举类型等:
1
2
3
4
5
6
7
|
for i in diamond: print (i) diamond.YELLOW diamond.BLUE diamond.GREEN diamond.RED |
枚举类型之间不能做大小的比较,可以做等值的比较;枚举类型和枚举值之间不能做等值的比较;枚举类型可以做身份(is)的比较。不同枚举类之间的枚举类型不能比较。从枚举值获得枚举类型:
1
2
3
4
5
6
7
8
9
10
11
|
class diamond(Enum): YELLOW = 1 BLUE = 2 GREEN = 3 RED = 4 a = 1 print (diamond(a)) diamond.YELLOW # 从一个具体的值获得相应的枚举类型。很有用。 |
如果想要每个枚举类型的值都是int类型,可以引入from enum import IntEnum
,在枚举类的括号里为IntEnum
。如果不想出现两个枚举类型出现同一个值(会报错),可以引入一个装饰器:
1
2
3
4
5
6
7
8
9
|
from enum import Enum from enum import IntEnum, unique @unique # 装饰器 class diamond(IntEnum): YELLOW = 1 BLUE = 2 GREEN = 3 RED = 4 |
原文链接:https://segmentfault.com/a/1190000014052535