Python入门细节

python入门细节

相除后的类型

type(2/2)
float
type(2//2)
int

双斜杠是整除,出来的类型是int。单斜杠的出来的是float类型。java

进制表示和转换

进制表示:python

  • 二进制:0b
  • 八进制:0o
  • 十六进制:0x

进制转换:c++

  • 转换为二进制:bin()
  • 转换为八进制:oct()
  • 转换为十进制:int()
  • 转换为十六进制:hex()
  • 转换为布尔类型:bool()

布尔类型

布尔类型转换:bool()windows

  1. 布尔类型属于数字这个基本数据类型里面
  2. 只要是非零的数字,bool类型的值为True
  3. 对于字符串,布尔类型为True,除了空字符串
  4. bool值为False:
  • bool(0)
  • bool('') 中间没有空格
  • bool([])
  • bool({})
  • bool(None)
  • bool(NoneType)
  • bool(set{})

多行字符串

三个引号能够再IDLE下回车不会执行换行。print函数能够输出n这样的反义字符。
单个引号想回车换行能够再前面加上字符便可。数组

'''
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都没有关系。app

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.字符串的'+'和'*'函数

"hello"+"world"
Out[1]: 'helloworld'

"hello"*3
Out[2]: 'hellohellohello'

2.获取字符串里的字符ui

"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 基本选取(切片)this

["新月打击","苍白之瀑","月之降临","月神冲刺"]
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]

#能够看到,切片操做很简单,第二个冒号后面的数字能够看做是步长。注意负数时的用法。

能够看到,当没有冒号的时候,单个选取出的是元素的类型。可是当有冒号的时候,选取出的是序列的类型。这里须要注意debug

1.1.2 列表的相加和乘法

["新月打击","苍白之瀑","月之降临","月神冲刺"]+['虚弱','点燃']
Out[14]: ['新月打击', '苍白之瀑', '月之降临', '月神冲刺', '虚弱', '点燃']

['虚弱','点燃']*3
Out[15]: ['虚弱', '点燃', '虚弱', '点燃', '虚弱', '点燃']

1.1.3 判断元素是否存在

  • 运用in和not in便可
3 in [1,2,3,4]
Out[21]: True

3 not in [1,2,3,4]
Out[22]: False

1.1.4 计算长度,最大小值

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()
能够向列表中追加元素。

a = [1,2,3,4]

a.append('5')

Out[22]: [1, 2, 3, 4, '5']

2.元组(tuple)

  • 元组的操做,包括访问,加,乘,in等操做和列表是相同的。
  • 须要注意一点是:
type((1))
Out[16]: int

type(('sd'))
Out[17]: str

type((1,2,3))
Out[18]: tuple

若是括号里有一个元素,默认为是一个运算,不会认为是元组的括号。若是要定义只有一个元素的元组的:

type((1,))
Out[19]: tuple
# 括号里面什么都没有表示一个空元组
type(())
Out[20]: tuple
  • 元组是序列,不可变类型,可是若是元组里包含了列表,好比:
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} - {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()
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++不同。
  • 值类型和引用类型
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的值都会变化。

再进一步的,能够看如下代码:
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++中相似。
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值。

a = 1

a in {1:'1'}
Out[7]: True

a = '1'

a in {1:'1'}
Out[9]: False
  • 身份运算符

is, is not
身份运算符比较的是身份而不是值,简单来讲就是内存地址。和关系运算符“==”不同。

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()这个函数。

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函数

用来查看模块或者类内部的变量,包括系统内置变量。

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__的应用技巧

if __name__ == '__main__':
    pass

#来判断模块是否被看成入口文件被调用,若是被当作模块就不会打印if条件成立执行的语句,若是被当作入口文件才会执行

1.模块导入的方法

# 由父包引入子包或者同级别引入的状况

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

该文件,能够在导入一个包,或者导入包中的函数的时候,系统会首先执行该文件。

from packet import *  # 这行代码会引入被引入包中__init__.py中__all__指定的模块。

3.模块内置变量

a = 2
b = 1
infos = dir()
print(infos)

['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'b']

# 上方除了'a','b'都是系统内置的变量。

下面介绍几个比较重要的内置变量,利用import一个模块的时候会执行该模块里面的内容的机制。对于入口文件和模块文件,内置变量的值有所不一样。

  • 模块文件
'''
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入口文件和普通导入的模块文件是有差别的。

'''
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.绝对导入和相对导入

导入机制:

  • python模块导入时的搜索路径:

    1. 程序主目录,执行程序是包含执行代码文件的目录,交互模式下为当前工做目录,优先级最高
    2. PYTHONPATH中的目录
    3. 标准连接库目录,就是python的安装目录,源码在里面
    4. 3.x 中能够用.pth 文件
  • 以上这些构成了sys.path。你写的模块存储路径在sys.path 里面就能够import。
  • 绝对导入是指从入口文件引入执行文件所在的文件夹的包中的模块的时候,须要进行绝对导入。from ... import...中若是没有出现.模块名,也是绝对导入。
  • 相对导入指从模块引入父级的模块时,须要进行相对导入。在入口文件切记不能使用相对导入,即带.号。好比你单独运行某个模块,可是在这个模块里你用了相对导入,那么就会报错。可是你在外面引入这个模块的时候是不存在问题的。
  • 注意在模块下面,最好不要有和模块名重名的文件。
  1. 顶级包与入口文件main.py的位置有关,与main.py同级的包就是该包下全部模块的顶级包。而对于入口文件来讲不存在包的概念。
  2. 绝对导入/绝对路径:从顶级包到被导入模块名称的完整路径。注意必定是完整的路径。
  3. 相对导入,一个'.'表示当前包,两个'..'表示上一级包.'...'上上级包,以此类推。

注意:

  • import不支持相对导入,只能使用from import格式实现相对导入。
  • 入口文件中不能使用相对导入,由于它没有包的概念。
  • 使用相对导入不要超出顶级包,和入口文件同级都不能使用相对导入
  • pycharm中,你打开一个工程,你的入口文件处的sys.path中会自动添加该工程中的目录,因此假设你在模块a中相对引入其余模块b,模块b所在的位置超过了你如今所运行的文件(入口文件)所在的位置,可是只要不超过当前的工程目录所在的位置,仍然能够进行相对导入。

函数

注意事项

  • python默认有一个递归次数限制来防止无限递归调用,但能够设置递归最大次数:
import sys
sys.setrecursionlimit(10000)
# 能够设置最大迭代10000次。不过理论上虽然设置这么多,但实际上仍然达不到容许迭代这么屡次。
  • python中for循环内定义的变量能够在外部使用,这点和c和java不相同。
  • 若函数体中没有返回值,则认为返回None。

1.return 返回多个值,链式赋值和序列解包

python函数返回多个值直接在return后面用逗号分隔要返回的值便可,返回结果是tuple元组类型。比较好的接收函数返回的多个值的方法不是用一个变量接收元组而后用序号访问它的元素,而是直接用多个值接收而后分别使用这些变量,如:

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)

上面的接受参数的方式叫作序列解包。

  • 链式赋值和序列解包
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.函数参数

函数参数有:

  • 必须参数:形参和实参。
  • 关键字参数
  • 默认参数
  • 可变参数
  • 可变参数能够解开可变,而且能够进行可变关键字参数。定义可变参数后,传值的时候能够什么都不传,这个时候是空元组或者空字典。
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 : 占卜||

# 和传入元组同样,解序列能够传入两个*。
  • 形参是定义函数的时候定义的参数,实参是调用函数的时候传递的参数。
  • 关键字参数经过指定形参来进行参数赋值。
  • 可变参数在必须参数以后,默认参数以前,不然会出现赋值的错误
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中,类与对象的变量查找是有顺序的。
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.类方法

  • 定义类方法:
class Student
    sum = 0
    @classmethod
    def student_sum(cls):
        pass
    
# cls能够更换,函数上面是一个装饰器,表示了这是一个类方法。cls换成self或者其余任意的都是能够的,和实例方法中self能够任意替换别的字符是同样的。

对于类方法,调用类变量:cls.类变量 便可。因此cls表明的就是所属于的类。在调用类方法的时候,能够 Student.studen_sum 也能够经过对象来调用,即:student1.student_sum。可是建议用类名来调用比较好。

5.静态方法

  • 定义静态方法:
class Student
    sum = 0
    @staticmethod
    def add(x,y)
        pass

静态方法上一样要有装饰器来修饰,可是函数中不用显式的传入self或者cls,更像是一个普通的函数。在类方法和静态方法中都不能调用实例变量。通常不建议用静态方法,类方法更方便。

6.成员可见性

在python中,实际上没有什么是不能访问的。成员可见性更像是一种标志,一切全靠自觉。定义私有变量或者方法的时候,只须要在变量或者方法前面加上双下划线就表明了私有(注意不要同时在后面再加上双下划线)

# 咱们定义一个学生类

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。
# 定义一个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中的枚举类型实际上是一个类。

from enum import Enum

class diamond(Enum):  # 必需要继承父类Enum
    YELLOW = 1
    BLUE = 2
    GREEN = 3
    RED = 4
print(diamond.YELLOW)


diamond.YELLOW

# 可见打印出的就是diamend.YELLOW
  • 在枚举类型中,每一个类型有不一样的值,不容许出现相同类型赋不一样值,值能够是任意类型的。若是出现了两个枚举类型的值相同,下面的枚举类型会被当成是上面枚举类型的别名。
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是会报错的。
  • 类型最好用大写表示,表示为常量不能修改。
  • 枚举类型,枚举名称,枚举的值,代码以下:
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循环得到枚举类型等:
for i in diamond:
    print(i)
    
diamond.YELLOW
diamond.BLUE
diamond.GREEN
diamond.RED
  • 枚举类型之间不能作大小的比较,能够作等值的比较;枚举类型和枚举值之间不能作等值的比较;枚举类型能够作身份(is)的比较。不一样枚举类之间的枚举类型不能比较。
  • 从枚举值得到枚举类型:
class diamond(Enum):
    YELLOW = 1
    BLUE = 2
    GREEN = 3
    RED = 4
a = 1
print(diamond(a))


diamond.YELLOW
# 从一个具体的值得到相应的枚举类型。颇有用。
  • 若是想要每一个枚举类型的值都是int类型,能够引入from enum import IntEnum,在枚举类的括号里为IntEnum
  • 若是不想出现两个枚举类型出现同一个值(会报错),能够引入一个装饰器:
from enum import Enum
from enum import IntEnum, unique

@unique # 装饰器
class diamond(IntEnum):
    YELLOW = 1
    BLUE = 2
    GREEN = 3
    RED = 4
相关文章
相关标签/搜索