Linux下使用Python自带命令提示符,不能删除输错的命令,先安装yum install readline-devel -y,然后重新编译就可以了。

Python内置数据类型

#程序注释,用于写上程序说明信息。注释不会被Python解释器当做代码执行
import module_name 导入模块

数字

整型(int)

  • 0b 表示为二进制数
  • 0o 表示为八进制数
  • 0x 表示为十六进制数
  • 十进制不需要前缀
  • True=1 / False=0

将字符串转换为十进制int('字符串',进制类型),例如int('0b1111000',2)结果是十进制120,字符串中0b可不加int('1111000',2),像oct(i)其它进制转换方法也是如此。将含有整数字符串转换为小数可以用float('str')。
要将十进制数输出为其他进制时可以用:bin(i)二进制,oct(i)八进制,hex(i)十六进制,里面的i可填十进制或变量。

Python里加减乘除=+-*/,整数乘一个小数结果会是float(11.0),2/2整数除整数结果会是float,要想得到int需要多加一个/(整除),例如2//2这样就只会保留整数部分。还有其他运算符,幂运算**比如2**8,当x*y这个y是小数则是x开平方运算,取模(模运算)%也就是取余数比如17/2

这些运算符还有优先级(下图优先级从低到高),这里是官方文档,如果要想指定优先级可以用(),比如(4 * (5 - 1)) ** 2
Operator_Precedence.png

最后一条叫做布尔值对应着整数中1和0,它们也可以用于计算(True + True) + 1

浮点(float)

  • 3.14

对应生活中小数

math模块一些方法可以对int和float使用。

improt math #要想使用得先导入module
math.trunc(3.99) #取值朝向0,结果3
  math.trunc(-0.5) #取值朝向0,结果0
  math.trunc(-1.3) #取值朝向0,结果-1
math.ceil(3.99) #不管多大总是向上取整数,结果4
  math.ceil(2.5) #结果3
math.floor(10.99) #不管多大总是向下取整数,结果10
  math.ceil(5.69) #结果5
round() #四舍五入

复数(complex)

  • Python中表示复数10j在后面加上j。
    用得少

布尔(bool)

  • True,False

如果一个人突然冲到你面前兴奋讲道:明天你会变得很有钱!根据你现在程度会想这不可能是真的。这是生活中的真假,在Python中表示假就会用到False,真代表True
如果要测试一结果可以用bool(value)方法,value如果有值就是True,如果填0、0.0、False、空对象(空字符串...)、None其中任意一种就是False。

如果不清楚数据是什么类型,可以用type()来确认,例如>>> type(0o144),所有数据类型都可以用这个内置函数判断类型。

>>> b = 1
>>> b += b >= 1 #b>=1是True,然后b=b+True。
>>> b += b < 1 #b<1是False,b+False还是1。

(1,2,3)<(1,3,2) #一位一位去判断,到2<3为True就停,后面3<2就不判断。

1 and 0 #计算时需要看第二个值,所以返回0。
  1 and 2 #计算时需要看第二个值,所以返回2。
  0 and 2 #计算时看第一个值,所以返回False,第一个值都为False第二个值就不用看了。
1 or 0 #计算只看第一个值,不需要看第二个值返回1(称作短回环)。
  3 or 1 #看第一个值为True就不需要看第二个值,返回3。
  0 or 1 #这时第一个是False,需要看第二个值是什么,返回1。
not not True #结果True,第一次not是False,第二次not就是True了。

变量

一个值经常需要引用就可以存到变量中。

关于变量命名,多个单词的话个人喜欢以下划线分隔,下面示例中第一种。

couse_color
CouseColor
couseColor

其实只要以字母或下划线开头命名就是正确的(下划线开头在Python中有特殊含义),还有命名要让人能一眼看出什么意思(文件命名也是同理),变量名中不能有空格,还要避免与Python关键字(也称作保留字)和函数名,比如关键字if,另外变量名区分大小写。

Python数据类型是看值,不是看变量类型。

变量值是在内存开辟一块空间进行存储,当变量指向一个新值,原来值会被垃圾回收机制在计数器为0的特定时间进行回收。

共享引用:多个变量指向同一个对象(值)a='x' b='x',他们在内存中使用是同一块地址,用a is b判断他们在内存中是不是使用同一块地址,id(object)也可以返回地址。只有0-255的数字会被Python缓存,短字符串也会被缓存,并没指定长会被缓存。

int str tuple值类型不可变,list set dict引用类型可变

>>> a = 1
>>> b = a
>>> a = 3
>>> print(b) #结果是1

>>> a = ('A','B',[1,2,3])
>>> b = a
>>> a[2][0] = '1'
>>> print(b) #结果会随a变化

序列(sequence)

序列是指有序排列,每个元素都有序号,用下标访问序列元素。下面是一些通用操作(用列表和字符串举例)。

#通过索引访问某一个值
>>> ['Am', 'B7', 'Cadd2', 'Dsus4'][0] #得到'Am'。这样通过正数序号获得数据称作正向递增序号。
>>> ['Am', 'B7', 'Cadd2', 'Dsus4'][-1:] #得到['Dsus4'],加上冒号它就返回列表。这样通过负数序号获得数据称作反向递减序号。
#如果是-2会是倒数第二个值,从右向左访问,以此类推。

#对元素进行切片(获取片段数据)
'String'[:7] 
#索引0开始到第7个取出,可没有第7个字符,不管结束范围再大只会返回所有字符,结果为String。
  'String'[0:4] #访问0到3这四个元素,4是指4前面的元素并不包含4,结果是Stri。
  'String'[0:-1] #访问Strin。
  'String'[:6] #这一段是说从前面0开始到第6前面字符都拿出来,返回String。
  'String'[-3:] #访问ing。
  'String'[:] #访问所有元素。

#按步长(stride)访问范围内的值
'String'[::2]
#访问所有元素。从0开始算每两步取1,不填步长默认是1,结果是'Srn'。
#step可以为负数你知道吗?,str_give='123456789'怎么取出987?可以用str_give[:5:-1]惊了。

#判断某个内容在序列内,返回bool。
'内容' in 序列
'内容' not in 序列

#两个列表合并
>>> popular_chord = ['Am', 'B7', 'Cadd2', 'Dsus4']
>>> sevent_chord = ['Cm7']
>>> popular_chord + sevent_chord
>>> ['Am', 'B7', 'Cadd2', 'Dsus4', 'Bdim']
#不会对原来数据做更改,只是返回一个新列表。

#重复出现多次
>>> Chord = ['C', 'Dm', 'Em', 'F', 'G', 'Am', 'Bdim']
>>> Chord * 3 #chord内数据重复3次,返回一个新列表。

#返回序列长度
len(obj) #会返回元素个数

#获取大小和总值
max(obj) #大
min(obj) #小,字符串则是比大小ASCII码
sum() #求综合运算后的值

#返回值第一次出现索引位置
sequence.index(value)

#返回值出现的次数
sequence.count(value)

可变序列通用操作

#改变某一个索引下的值
s[i] = new_value 

#改变某个范围索引下的值
s[i:j] = t #t是可迭代对象(想象成列表)
  >>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  >>> #a[:3] = [] 前三个值替换为空
  >>> #a[:3] = ['a'] 前三个值替换为a,注意不是每一个
  >>> #a[:3] = ['ABC','ABC','ABC'] 前三个值每一个替换成ABC
  >>> #a[:3] = 'ABC','ABC','ABC' 在练习中发现这样写也可以,不知有什么隐患。
s[i:j:k] = t #这样写要知道经过步长筛选后有几个元素被选中
  >>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  >>> #a[::2] = ['A','B','C','D','E'] 
  >>> #将02468替换成ABC,因为步长选中了这些元素,替换值个数必须要跟他相等

#删除
del s[i]
del s[i:j]
del s[i:j:k]
s.remove(x) #删除一个指定值,如果有多个相同值,会删除第一个匹配到的值
s.clear() #清空序列,s[:] = []效果一样。

#弹出(删除)一个值
s.pop([i]) 
#[i]可选的意思,i是指定index,默认返回末尾的值。

#增加一个值
s.append(x)

#增加多个值 
s.extend(x)
  >>> a.extend(['a','b','d'])
  >>> a
  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'd']


#增加一个值到指定位置
s.insert(i,x) #如果索引值超过序列长度,值会放在末尾。
s[i:i] = [value]
#如何插入值到嵌套列表

#复制序列
s.copy() #s[:]也可以,这样复制后是在内存开辟不同空间进行存储。

列表(list)

  • ['字符串', 1, True, ['这是嵌套列表']]

列表是一组有序数据,里面可以存放不同类型的值,通过下标引用数据。

常用操作

list(range(5)) #生成列表,range生成0-4五个int值。

#排序
s.reverse() #从小到大更改原来序列,不返回值,如果再次使用会再次翻转。
s.sort(revers=False) #默认升序,revers=True是降序。
#我试了试不能int和str混合进去排序,只能单一进行。

sorted(t) #不改变原有序列,降序操作后返回一个新list
  >>> a
  [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
  >>> sorted(a)
  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

集合(set)

  • {1, 2, 't', True}

无序 去重

{1, 2, 3, 4, 5, 6} - {3, 4} #找出差集
{1, 2, 3, 4, 5, 6} & {3, 4} #找出交集
{1, 2, 3, 4, 5, 6} | {3, 4} #找出并集

set() #定义空集合

字典(dict)

  • {key: value, key: value, {key1: value,key2: value}}

它是无序集合,因为你创建的顺序和最终呈现顺序可能不一致,字典表中可以存任何类型对象。
字典key一定是不可变类型,list就不可以做为key(通常都用字符串)。

#声明字典表
>>> {} #空字典
>>> {'云': '雨', '雪': '空', '晚照': '晴空', '来鸿': '去燕', '宿鸟' :'鸣虫'}

#可以转换为字典
dict(key=value)
>>> dict(name=9521,age='no')#默认key会被转成string。
{'name': 9521, 'age': 'no'}

>>> dict([('name',9521),('age','none')]) #有规律可迭代对象会被转成dict。
{'name': 9521, 'age': 'none'}


#将序列作为key
>>> dict.fromkeys(['name',9521,'age','no'])
{'name': None, 9521: None, 'age': None, 'no': None}

#通过key访问数据
>>> a = {'title': '乡土中国', 'auther': '费孝通', '分类':{'类别': '社会学'}}
>>> a['分类']['类别']
>>> '社会学'

get(key[,default]) #key不存在就返回default值,没写default值就返回None。
>>> a.get('分类'['类别'], '这个key不存在') 

#获取所有keys和values
dict.keys() #获取所有key,返回类型是dict_view不是dict。
dict.values() #获取所有value,返回类型是dict_view不是dict。
dict.items() #获取所有key和value,返回类型是dict_view不是dict。

#返回字典长度
len(dictview) #有多少组数据

#复制字典
dict.copy()

#清空字典
dict.clear()

#更改数据或添加数据
dict['key'] = 'new_value'

#合并字典
dict.update(new_dict) #将new_dict插入到dict中,不会对new_dict有影响。

#删除内容
dict.pop('key'[,defalut])
#pop()会返回键的值,键不存在就抛错,default在key找不到返回这个值,None不返回消息。
dict.popitem() #弹出整个键值对,3.7中从后向前,<3.7随机弹出。
del dict['key'] #删除这个键,包括值。

#查询key在不在dict内,返回布尔值。
'key' in dict
'key' not in dict

不可变序列

字符串(str)

  • "1'0" 加上引号就是字符串,'1"0' 单引号也可以。
  • '''三引号中内容也是字符串(当然也可以用3个双引号啦),它表示多行文本,在没有任何操作的情况下可以当作注释用,但本质上还是字符串'''

文字Python中叫字符串。
将数字转换成字符串可以用str(number)函数,里面的number可以填上数字,十进制可以不加前缀,其他进制要加上例如:str(0b1111000)

写的东西多了挤在一行,自然就看不得不顺眼,这里就可以用多行字符串来写代码,'''"""都可以。

#Linux Style
>>> '''
... "H"e'll'o World
... 这是三引号中的内容
... '''
'\n"H"e\'ll\'o World\n这是三引号中的内容\n'

#IDLE Style
>>> '''
Hello World
这是三引号中的内容
'''
'\nHello World\n这是三引号中的内容\n'

难道就你三引号能表示换行吗?这里的单引号也可替换成双引号。

>>> 'Hellow\
...  World'
'Hellow World'

n(换行)和r(回车)它俩不是一个概念,r指定一段字符放入行首,会根据字自身字符长度替换行首长度相等字符,多出来的显示在后面,实际场景不知道怎么用。

>>> print('1234567多\rtest123')
test123多

转义符
r'\n \t \r'在开头用r表示原生字符串(raw),大写R也可以,就只是把引号内的字符当做字符串。

下面这条就是错误示范

print(r'字符串'还是要遵循语法规范开头结尾闭合后才算完整,字符串都无法构成怎么算原生字符串呢?')

b'azx129'以b开头是字符串声明为字节方式,内容要在ASCII码范围内或者是十六进制信息。

常用操作

#类型转换
str() #相当于在两侧加上引号
ord(x) #将字符x转换成Unicode码
chr(u) #将Unicode编码对应的字符展示。

#字符串替换
str.replace(old, new[, count])
  'SStrinSg'.replace('S', 'A', 1) #把S替换为A只替换1位,返回一对新字符串。
'A' + 'String'[1:] #把第一位替换成A。

#首字母大写
str.capitalize()

#整体转换大小写
str.upper() #大写
str.lower() #小写

#判断开头结尾
str.startswith('str') #判断以什么开头,返回bool。
str.endswith('str') #判断以什么结尾,返回bool。

#判断整体是否为数字或字母
str.isnumeric() #是否是数字,返回bool。
st.isalpha() #是否是字母,返回bool。

#居中字符串
str.center(width,[fillchar])
>>> "String".center(20, '=') #String加上填充它的宽度一共20个字符,在String居中后两侧用=填充。

#去除左右侧字符
str.strip(chars) #去除str左右侧中指定chars。

#拆分连接字符串
str.split(str, [num]) #str是参照什么分隔,num默认分隔分割所有指定的字符,返回列表。
  >>> "S,:asd.TR,NAG".split(':') #以冒号分隔
  ['S,', 'asd.TR,NAG']

  >>> "S,:asd.TR,NAG".split(',') #以逗号分隔
  ['S', ':asd.TR', 'NAG']

  >>> "S,:asd.TR,NAG".split(',', 1) #以逗号分隔我只分割一次后面的不管。
  ['S', ':asd.TR,NAG']

  >>> "S,:asd.TR,NAG".split() #不填参数把str整个作为参数返回。
  ['S,:asd.TR,NAG']

str.join(s)
  >>> ' '.join('setlkajsdf') #序列除了最后一个元素,其他元素以空格作为分隔符进行连接。
  's e t l k a j s d f'

格式化

str.format() #下面是它的语法。
官方原文(有删减):[[fill][align][width][grouping_option][.precision][type]
中文解释:{:<填充><对齐><宽度><千位分隔符><精度><输出类型>}
官方文档:https://docs.python.org/zh-cn/3/library/string.html#formatspec
字段解释
fill(填充)可以是任意字符,默认是空格。
align(对齐)<左对齐 >右对齐 ^居中
width(宽度)填充字符总长度,会算上后面参数长度。例如str我填充20,这20就包括了str。
grouping_option(分组)主要针对整数进行千分位,例如:4000表示为4,000。
.precision(精度)表示保留小数点后几位.2就保留2位。
type(类型)b=二进制,c=unicode字符,d=十进制,o=八进制,xorX=十六进制。——整数
e或E=科学计数法,f=定点数表示方法,不填精度默认为6位,%=把数乘以100进行显示百分比,比如1=100%。——浮点数

格式化字示例:

>>>'name:{0} age:{1} job:{2}'.format('value1', 'value2', 'value3')
'name:value1 age:value2 job:value3'
#它可以把花括号作为一个占位符,意思就是这个地方我先占着,一会儿我放东西进来。

>>>'name:{1} age:{1} job:{2} test:{0}'.format('value1', 'value2', 'value3')
'name:value2 age:value2 job:value3 test:value1'
#也可以重复使用一个值

>>>'name:{} age{} job{}'.format('value1', 'value', 'value3')
'name:value1 age:value job:value3'
#它的默认顺序就是一对一,所以可以不填。
#前面的占位符数量要和后面参数匹配。不然就抛出错误

>>>'age={} job={} 一会儿再来={hold}'.format('value', 'value', hold='没问题')
'age=value job=value 一会儿再来=没问题'
#也可以在占位符内填写变量一会赋值,总感觉这样可能会忘记,不过灵活是真的。

>>>'name:{hold} age{}  一会儿再来:{}'.format('value1', hold='没问题', 'value3')
'name:value1 age:value job:value3'
#占位符和值一定要对应,不能第一个占位符写hod变量,值却写在第二个,
#一定要一一对应,因为它默认就是一一匹配。

>>> '{:=^20.2f}'.format(1.12312)
'========1.12========'
#这条是针对小数,用=填充,^居中显示,数量20,.2是指显示小数点后2位,f代表这个变量放入槽中的类型。

>>> '{:=^20,}'.format(1502157.12312)
'==1,502,157.12312==='
#用千分位显示。

元组(tuple)

  • (123,123.0,'123', True,['a','b'],('cool',1))

元组内容定义后不能改值,元组只有一个元素需要在元素后面加上逗号(1,),以区分做数字运算(1+1),不然默认为int类型。
字符串和元组是不可变类型!!!这里不可变是指不可改变值。

#声明元组
a = 1,2 #可以省略,只要前后语法不冲突,但建议加上元括号。
a = 1, #一个元素,如上。

#声明空元组
empty_tuple = ()

tuple() #转换为元组

#一个奇怪的赋值,官方文档称为元组封装,序列解包。
>>> t = 12345, 54321, 'hello!'
>>> x, y, z  = t #不一定是变量也可是具体值,x, y, z  = 'A', 'B', 'C'。
>>> print(x, y, z)
>>> 12345 54321 hello!
#只要右侧序列与左侧元组个数相等就可以赋值,已测试str set list tuple dict都可实现。

#下面再记录几个好用的赋值姿势。
>>> x, *y, z  = "四个字符" 
#'四'赋给x,'个字'赋给y并返回时list,'符'赋z,这种称作序列赋值。

>>> a, b = 10, '11'
>>> a, b = b, a #这样a,b就可以交换变量值


>>>x = y = 5
#将多个变量赋同一个值。y=5,x=y,它们都是5,称作多目标赋值。

>>> x+=10
#等于x = x+10,称作参数化赋值。+ - * / % ** //等运算符都支持。

range(范围)
用于生成数值序列,类型是range。range(start, stop[, step]),start从几开始stop到多少结束(范围只填一个数默认是0),开始到结束来取这个范围,step是步长这个和序列中切片是一个意思,默认是1。

>>> tuple(range(10)) #默认从0-9不包含10
>>> tuple(range(1, 21)) #从1-20
>>> tuple(range(1, 21, 3))从1-20每隔3步取一位。

range是不可变序列,不支持原位改变值,但能支持通用序列操作

>>> a = range(1, 11)
>>> a[-1]
>>> 10
>>> a[-1] = 111
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'range' object does not support item assignment

文件读写

open('Path','Mode',encoding='') #创建实例
r=read w=write a=append 不填写模式默认是r
如果是二进制要在模式后面加b,如rb。

obj.read() #一次性读取所有实例内容,对大文件需要内存更大。
obj.read(n) #一次性读取一段信息,n读取的数量取决于你的模式是字符还是字节。
obj.readlines() #读取多行内容,返回列表。
obj.readline() #读取一行内容,返回列表。
#读取完成后指针移动到内容后面,再次读取也是空的。

obj.tell() #可以读取文件指针位置。
obj.seek(0) #将指针移动到内容开头,避免在文件读取完后继续读取是空的情况。

obj.close() #关闭连接,为了规范操作尽量关闭。

improt os #导入os模块
os.getcwd() #获取当前目录
os.chdir(path) #更改目录

obj.write() #写入一个字符串
obj.writelines() #写入列表中提供的内容
 
obj.flush #将内存中数据写入硬盘,不然就close()结束操作来写入。

条件与循环

表达式:由运算符和操作数所构成的序列
代码块(语句):多个表达式构成代码块
左结构:从左到右运行
右结构:从右到左运行

if

Python中if小技巧

if condition:
    pass #占位语句或者是空语句,没有任何作用。
else:
    ... #等同于pass

if condition:
    pass
elif:
    pass #elif相比大量if else代码量更少。
else:**加粗文字**
    pass #一定是和if一起出现,不然会报错。

Python有类似其他语言里三元运算符。
true_expression if condition else false_expression

>>> 'yes' if 1 > 2 else 'no'
'no'
>>> 'yes' if 1 > 0 else 'no'
'yes'

while

写while要注意避免死循环,做法是在代码块中改变常量。条件为False循环停止,True就一直执行。

while condition:
    pass
else:
    pass #else循环结束执行
#Ctrl+c键盘终止死循环(keyboardInterrupt)

for

for主要用于遍历序列内的元素。

a = [1,2,3,4,5,6,7]
for x in a:
    if x == 6:
        continue
        #跳过本次循环,不执行continue下面代码,回到外层循环继续运行。
    print(x, end=' ')

1 2 3 4 5 7


for x in a:
    if x == 5:
        break #强制中断循环
    if x == 1:
        continue 
    print(x, end=' ')

2 3 4

这里嵌套循环break没有只输出apple,是因为for i in a把第一个list先存进i再进行嵌套中for x in i,if判断发现list中有orange就break,重新回到for i in a,把元组放入i,进行for x in i。所以第一次循环不是一次性把list和tuple放入i,而是一组一组来。
补充:break只是中断了嵌套循环,不影响外部循环。2019.5.31

a = [['appale', 'orange', 'bannana', 'grape'], (1, 2, 3)]
for i in a:
    for x in i:
        if x == 'orange':
            break
        print(x, end=' ')
else:
    print('EOF')

appale 1 2 3 EOF 

函数

help()查看内置函数说明
import this查看Python之禅

def function_name():
    pass #函数参数可以省略的

def function_name1(parameter_list, parameter_list2):
    pass #代码块
function_name1(parameter_list2="详细指定", parameter_list="参数位置")
#传参时可以明确指定参数方便阅读
function_name1(parameter_list2="详细指定", "参数位置")
#如果你先传parameter_list2参数值,第二个值不写,它会抛错,
#因为它不知道这个值是谁的,应该像上一条那样两个值都明确指定。
#另外定义函数设有几个参数,调用就要写几个参数,不然抛错。

def function_name2(parameter, parameter2, parameter3="Default"):
    print(parameter, parameter2, parameter3)
function_name2(parameter3="新值3", 2, 1)
#调用不能先传默认值,应依照定义参数顺序传递。

function_name2(parameter2="新值2", parameter1="新值1")
#这样传参称为关键字参数,对所有必须要传的参数指定了关键字。
#parameter2是关键字参数,它的值是:新值2,填写前后顺序并不重要。

def function_name3(parameter, parameter2="Default"):
    pass
function_name3(1)
#可以给位置参数写个默认参数,在调用时就不用填了,

def function_name4(parameter, parameter2="Default", parameter3):
    pass
#这样写会抛错,填写一定是位置参数在前,默认参数在后,不能混着。

def function_name5(parameter, parameter2="Default"):
    print(parameter, parameter2)
function_name5(parameter="值1", "值2")
#这样写是错的,官方语法规定:在函数调用中,关键字参数必须跟随在位置参数的后面。

函数中没有return就返回None,返回多个值用逗号隔开,用逗号隔开类型是tuple,其实也可以返回其他类型比如list只不过逗号比较方便。

def fars(a, b):
    return a,b
fars(b=1, a=2)
(2, 1)

a = fars(b=1, a=2)
print(a, type(a))

函数中内部变量和函数外定义的全局变量是不同的,函数执行完成局部变量会被垃圾回收。
函数内部可以访问全局变量,要在函数内部重新改写或者定义全局变量需要加上global关键字。

#使用模块中现有的全局变量
a = 1
def test():
    global a
    a += 4
    return a
>>> print(a, test(), a)
1 5 5

#用global定义全局变量
def test():
    global c
    c = 1

test()
>>> print(c)
1

在嵌套函数中使用外层函数变量要用nonlocal保留字。

#使用nonlocal
def test():
    a = 10
    print('test值:' + str(a))
    def test_local():
        nonlocal a
        a += 1
        print('test_local值:' + str(a))
    test_local()
>>> test()
test值:10
test_local值:11

另一个要提的是作用域链,下面是一个示例。
打印3

c = 1

def func1():
    c = 2

    def func2():
        c = 3
        print(c)
    func2()

func1()

此时打印2,因为相对于func2来说func1中变量C是全局变量

c = 1

def func1():
    c = 2

    def func2():
        # c = 3
        print(c)
    func2()

func1()

此时打印模块中全局变量C的1,会一层一层往上找,这个称为作用域链。

c = 1

def func1():
    # c = 2

    def func2():
        # c = 3
        print(c)
    func2()

func1()

函数中使用一个与全局变量中同名变量,这个全局变量的值是可变序列类型,那就等同于函数外全局变量。哪怕是传进来个变量也是一样,因为都指向同一个引用(引用传递)。如果传进来是个不可变类型(值传递),就会自动copy一个副本来引用,和原来的变量完全独立。

a = ['123abc', 'ABC']
def test():
    del a [0]
    return a
print(a)
print(test())
print(a)
['123abc', 'ABC']
['ABC']
['ABC']

a = {'云':'雨'}
def test():
    a['云'] = 'raingray'
    return a
print('全局变量:', a)
print('函数进行修改:', test())
print('全局变量:', a)
全局变量: {'云': '雨'}
函数进行修改: {'云': 'raingray'}
全局变量: {'云': 'raingray'}

a = {'云':'雨'}
def test():
    new_a = a.copy() #重新复制一份,引用和原来互相不干扰。
    new_a['云'] = 'raingray'
    return new_a
print('全局变量:', a)
print('函数修改副本:', test())
print('全局变量:', a)
全局变量: {'云': '雨'}
函数修改副本: {'云': 'raingray'}
全局变量: {'云': '雨'}

可变参数
给函数传递多个参数,要注意的是参数一定是放在最后,位置参数-->默认参数-->任意参数(可变参数)。
另外一点是*名称必须出现在**之前。

#names函数把接收过来的实参当做元组,定义方式*。
def names(dec, *name):
    print(dec, name)
    print(type(dec), type(name))
>>> names("人物:", "富贵儿", "宋凡平", "宋刚", "李光头")
人物: ('富贵儿', '宋凡平', '宋刚', '李光头')
<class 'str'> <class 'tuple'>
>>> a = ("富贵儿", "宋凡平", "宋刚", "李光头")
>>> names("人物介绍:", *a) #可以传递变量,这个过程叫做解包。
人物介绍: ('富贵儿', '宋凡平', '宋刚', '李光头')


#实参接收过来当做字典,定义方式**。
def names(book, *dec, **name):
    print(book, dec, name)
>>> names('book', '名字', 人物='宋凡平', 人物2='宋刚') 
book ('名字',) {'人物': '宋凡平', '人物2': '宋刚'}
>>> a = {'人物1':'富贵儿', '人物2':'宋凡平'}
>>> names('book', '名字', **a) #解包
book ('名字',) {'人物1': '富贵儿', '人物2': '宋凡平'}

lambada表达式

lambada是个表达式不是代码块,内容一般是一些简单计算,这个lambada没有名字执行完返回一个function对象,为了引用会赋值给变量,它执行完成后会自己return,不需要手动定义。

lambda_exect = lambda 参数1, 参数2: 表达式
lambda_exect()

文档字符串(Documentation Strings)

模块、类、函数等添加,使程序易读易懂。
第一行是对函数功能进行描述,第二行必须是用来区分摘要和描述,从第三行往下是参数说明。

def print_num(x, y):
    """
    摘要:打印出。第二行必须是用来区分摘要和描述

    描述:这两个数都应该是整数
    :param x: 传输数值
    :param y: 传输数值
    :return: 没有返回值
    """
    print(x, y)


#help(print_num) 
print(print_num.__doc__)

    摘要:打印出。第二行必须是用来区分摘要和描述

    描述:这两个数都应该是整数
    :param x: 传输数值
    :param y: 传输数值
    :return: 没有返回值

module(模块)

Python组织结构:包——>模块——>类——>函数/变量

命名空间

不同包里都有相同名字的模块需要这样区分Apackget.module1,Bpacket.module1,在模块前加上包的名字,这样完整的路径称作命名空间。
包:一个包中会有__init__.py这个文件,没有就是个普通目录,(<=3.3必须存在>3.3可不写)__init__.py这个模块名字就是包的名字。

导入模块

一些需要重复使用到的代码,可以提取到独立模块中供其他模块调用,这样就避免代码冗余,做法是improt [包.][子包.]模块名,导入后用法是,packet.module_login.function比如user.login.checkpwd,这就是引用user包下login模块中checkpwd功能,这个功能有可能是函数或变量。

有时路径太长会很麻烦,比如packet.packet2.packet3.module_login.function,可以用packet.module_login.function as new_name,来取一个别名,使用时就可以用别名来调用功能。

#宁愿写的长一些,保守一点。
from 包 improt 模块 #导入模块和前面improt packet.module一样
from . improt 模块 #.代表当前包

#不建议使用,因为可能和现在编写的模块命名冲突
from .模块 improt 功能 #在现在包中模块下导入某个功能
from 包.模块 improt * #一次性导入所有变量和函数(不建议这样做),不会导入__version__双下划线开头和结尾这样的内容。
from 模块 improt 变量,函数 #导入对应的变量和函数
from 模块 improt (变量,函数,
变量2) #导入多个变量可以用小括号包裹起来进行换行。

__init__.py:包中__init__.py文件所在包或包内模块被其他模块导入时,这个文件会自动执行(用来初始化类)。既然导入会自动执行那其他模块中需要用的到模块直接在init中导入,在其他模块中导入这个包就可以用。
在文件中写入__all__ = ['模块名'] #别的模块在导入from 包 improt *所有模块,就只限定你填写的模块。

模块要避免循环导入,你在模块1中导入模块2,然后再模块2又导入模块1,这样就会出错,因为执行模块1时导入模块2,模块2进而又导向模块1

模块位置

sys.path变量值类型是列表,里面儿存着查找模块路径。Python从中查找模块位置。当导入一个模块时,Python解释器会先找内置模块有没有相同名称的,找不到会从sys.path变量中查找。
列表值sys.path[0]第一个值时运行Python脚本的目录,如果这个目录不能用会显示为空字符串,另外Python查找sys.path是从前到后找的。

import sys


print(sys.path) #为了方便显示列表我手动换行过。
['/root/PycharmProjects/LearnPython',
 '/root/PycharmProjects/LearnPython',
 '/usr/lib/python37.zip', '/usr/lib/python3.7',
 '/usr/lib/python3.7/lib-dynload', 
'/usr/local/lib/python3.7/dist-packages', 
'/usr/lib/python3/dist-packages',
 '/usr/lib/python3.7/dist-packages']

命令行参数

接受参数存在sys.argv,以供使用。

import sys


print(sys.argv)

sys.argv其中包含了被传递给 Python 脚本的命令行参数,也就是说module_using_sys.py后面才是参数。 argv[0] 为脚本的名称(是否是完整的路径名取决于操作系统)

root@gbb:~/PycharmProjects/LearnPython# python3 module_using_sys.py 1 2 3
['module_using_sys.py', '1', '2', '3']
root@gbb:~/PycharmProjects/LearnPython# 

name

全局变量 __name__,每个模块都有。可以用来获取一个模块名字。
用来控制那些代码能够被执行,我还想象不到实际场景。

if __name__ == '__main__':
    print('自己才能执行')
print('12321')

执行结果
其他人调用此模块执行的代码,打印12321是因为模块被调用时就执行里面儿内容,发现__name__变量值不是字符串'__main__'所以没执行。

root@gbb:~/PycharmProjects/LearnPython# python module_using_name.py 
自己才能执行

root@gbb:~/PycharmProjects/LearnPython# python3
>>> import module_using_name
12321
>>> 

dir()

内置dir() 函数能够返回当前或指定模块内定义的内容,其中包括函数内所定义的函数、类与变量。
如果向dir传递的参数是模块名,函数将返回指定模块的名称列表。 如果不传参,函数将返回当前模块的名称列表。

>>> dir()
['__annotations__', '__builtins__',
 '__doc__', '__loader__',
 '__name__', '__package__',
 '__spec__']
>>>
>>> a = 1
>>> def test():
...     pass
... 
>>> dir()
['__annotations__', '__builtins__', 
'__doc__', '__loader__', 
'__name__', '__package__', 
'__spec__', 'a', 'test']

面向对象(OOP)

类是一堆数据的一个概括,通过给类传递不同参数这个模板可以产生很多不同对象,这些对象有部分相同和不同的地方。
类是用来封装代码,不建议在类里运行代码。
要使用类需要实例化,就是通过实例化类来产生对象。

class ClassName(): #第一个字母大写,多个单词也是一样。
    a = '1' #可以定义多个变量
    b = '2'
 
    def print_text(self):#可以定义函数
        print(self.a) #调用类中变量要加self


define = ClassName() #实例化类
define.print_text() #调用ClassName中的方法

__init__方法 类变量 实例变量 实例方法。
__init__通常用来初始化实例变量。

类变量是和类相关的变量,实例变量是和对象有关(对象是实例化出来的)。

我们在每个方法括号中都加上了self,它所代表的就是创建当前实例的对象,下面代码中我们对ClassName()进行实例化赋给了变量define,当你define.a来调用实例变量a,其self就指的是define
如果要使用实例变量就要加上self来定义,例如:self.a

实例方法用来操作实例变量的。

在实例变量找不到变量会继续到类变量中查找,这种情况出现在对象操作上,比如define.a首先会在实例变量中查找,找不到就向类变量中查找a这个同名变量。

class ClassName():
    a = '' #类变量
    b = '' #类变量

    #定义构造函数,每个实例方法第一个参数必须指定,官方推荐使用self。
    def __init__(self, a, b):
        #实例化会自动调用构造函数,可以手动调用,但没必要。
        self.a = a #用self来存储实例变量的值
        self.b = b #用self来存储实例变量的值
        # print(self.__class__.a)#访问类变量
        # print(self.a)#访问实例变量
        return None #构造函数只能返回None,不能返回其他值。

     #定义实例方法,上面的init也是做实例方法,只是调用时有区别。
    def say_hi(self):
        print('可以调用init方法初始化的值:', self.a, self.b)

define = ClassName("值1", "值2") #构造函数定义几个参数就要传几个,不然抛错。
print(define.a) #访问实例变量a
print(ClassName.b)#访问类变量b
define.say_hi() #init实例化变量可以给所有实例方法使用

类方法用来操作类变量。

静态方法使用场景是当类和对象关系不大是使用,不推荐经常使用,和普通函数没有差别。

属性

class ClassName():
    a = 0

    def fun_test(self):
        pass

    # 类方法和实例方法的区别是加了@classmethod这个装饰器。
    @classmethod 
    def plus_number(cls): # 官方建议用cls,也可用其他参数名。cls代表当前类。
        cls.a += 1 # 用来操作类变量,也可以在实例方法操作(不推荐)。
        print(cls.a)

     # 静态方法装饰器@staticmethod
    @staticmethod
    def static_str(): #静态方法不强制传第一个值
        print("字符串")

    @property
    def art(self):
        print("调用可以不加括号当做属性来用,本质上还是方法,一般用于做些计算。")

    @art.setter #如果想赋值,属性一定要定义在前定义然后再加上setter。
    def art(self, value):
        print("可以接收到别人赋值,这是你传的值:" + str(value))


ClassName.plus_number() # 直接调用类方法,不推荐由去对象调用。
ClassName.static_str() # 通过类调用静态方法
static_obj = ClassName()
static_obj.static_str() # 通过对象调用静态方法
ClassName.art #调用属性
ClassName.art = '传值试试' #会自动打印你赋的值

成员可见性

主要防止从外部来更改数据,默认是public可以外部访问,双下划线开头就是private不能从外部直接访问。

class Student:
    def __init__(self):
        self.__score = 0

    def marking(self, score):
        self.__score = score
        print(self.__score)

    def __test(self):
        print('私有不可以访问')


student = Student()
student.marking(60)
student.__score
# student.__test


60
Traceback (most recent call last):
  File "asdf.py", line 14, in <module>
    student.__score
AttributeError: 'Student' object has no attribute '__score'

七月老师在课中说到Python是没有真正的private,上述代码可以用__dict__查看对象的属性。可以看到__score在实例化后名称被改做_Student__score,实例变量前面加上下划线和类名。

class Student:
    def __init__(self):
        self.__score = 0

    def marking(self, score):
        self.__score = score
        print(self.__score)

    def __test(self):
        print('私有不可以访问')


student = Student()
student.marking(60)
print(student.__dict__)
student._Student__test()
# student.__score
# student.__test


60
{'_Student__score': 60}
私有不可以访问

还有一点是要提到的,访问私有变量或方法会抛错,是因为到对象中去取一个不存在的属性,这在上面代码可以看到。那给它赋值再访问呢?

没错居然赋值成功了,这是由于Python动态性这个特性导致的,但是这个实例变量它和在实例方法中定义的可不一样,实例方法中定义的是private,我们在对象外面定义的是public,虽然名称一样,这个要区分开来。

class Student:
    def __init__(self):
        self.__score = 0


student = Student()
student.__score = 60
print(student.__score)
print(student.__dict__)


60
{'_Student__score': 0, '__score': 60}

类的继承
把现有模块中的类继承过来,把要继承的类称作基类/父类/超类,而当前类称作子类/派生类,继承后子类就能使用父类中所有的方法和变量了,避免重复编写相同代码。
每个模块中建议只写一个类,这样结构清晰。

标签: none

添加新评论