python基础
1.变量及数据类型
变量
变量命名规范
- 变量名只能包含字母、数字和下划线。变量名可以字母或下划线打头,但不能以数字打
头,例如,可将变量命名为message_1,但不能将其命名为1_message。 - 变量名不能包含空格,但可使用下划线来分隔其中的单词。例如,变量名greeting_message
可行,但变量名greeting message会引发错误。 - 不要将Python关键字和函数名用作变量名,即不要使用Python保留用于特殊用途的单词,
如print - 变量名应既简短又具有描述性。例如, name比n好, student_name比s_n好, name_length
比length_of_persons_name好。 - 慎用小写字母l和大写字母O,因为它们可能被人错看成数字1和0。
私有变量的定义
在Python中,有以下几种方式来定义变量:
- xx:公有变量
- _xx:单前置下划线,私有化属性或方法,类对象和子类可以访问,from somemodule import *禁止导入
- __xx:双前置下划线,私有化属性或方法,无法在外部直接访问(名字重整所以访问不到)
- xx:双前后下划线,系统定义名字(不要自己发明这样的名字)
- xx_:单后置下划线,用于避免与Python关键词的冲突
# PEP8代码格式:
1. 缩进
- PEP 8建议每级缩进都使用四个空格,这既可提高可读性,又留下了足够的多级缩进空间
2. 行长
- 很多Python程序员都建议每行不超过80字符。最初制定这样的指南时,在大多数计算机中,
终端窗口每行只能容纳79字符;
- 将程序的不同部分分开,可使用空行。
3. 类名
- 类名应采用驼峰命名法,即将类名中的每个单词的首字母都大写,而不使用下划线。实例名
和模块名都采用小写格式,并在单词之间加上下划线。
- 对于每个类,都应紧跟在类定义后面包含一个文档字符串。这种文档字符串简要地描述类的
功能,并遵循编写函数的文档字符串时采用的格式约定。每个模块也都应包含一个文档字符串,
对其中的类可用于做什么进行描述。
- 可使用空行来组织代码,但不要滥用。在类中,可使用一个空行来分隔方法;而在模块中,
可使用两个空行来分隔类。
- 需要同时导入标准库中的模块和你编写的模块时,先编写导入标准库模块的import语句,再
添加一个空行,然后编写导入你自己编写的模块的import语句。
5. 函数名
- 应给函数指定描述性名称,且只在其中使用小写字母和下划线。描述性名称可帮助你和别人明白代码想要做什么。给模块命名时也应遵循上述约定。
- 每个函数都应包含简要地阐述其功能的注释,该注释应紧跟在函数定义后面,并采用文档字
符串格式。文档良好的函数让其他程序员只需阅读文档字符串中的描述就能够使用它:他们完全
可以相信代码如描述的那样运行;只要知道函数的名称、需要的实参以及返回值的类型,就能在
自己的程序中使用它。
- 给形参指定默认值时,等号两边不要有空格:
- 如果程序或模块包含多个函数,可使用两个空行将相邻的函数分开,这样将更容易知道前一
个函数在什么地方结束,下一个函数从什么地方开始。
- 所有的import语句都应放在文件开头,唯一例外的情形是,在文件开头使用了注释来描述整
个程序。
数据类型
- 数值
- 整形:
int
- 浮点型:
float
- 注意:python2中
float
数据作除法得到的是整数,python3中得到的是小数。
- 注意:python2中
- 布尔型 :
bool
–True/Flase
- 复数型:
complex
(在python中用小写 j ,表示虚部,用其他的字母不行)
- 整形:
- 字符串(str)
- 列表(list)
- 元组(tuple)
- 集合(set)
- 字典(dict)
可变数据:list, dict, set
不可变数据:num, str, tuple
序列
- 字符串 str
- 列表 list
- 元组 tuple
- 可变序列:list
- 不可变序列:tuple,str
运算符
算术运算符:+ ,- , *, /, %, **,//
赋值运算符:= ,+=,-=, *=,/=,%=, **=
比较运算符:==,!=, >, <, >=,<=
成员运算符:in , not in
身份运算符:is , is not
判断两个名字是否指向同一个对象,当id相同时返回True(==比较运算是判断的值)
逻辑运算符:and,or,not
and(与) 两个条件都满足时才返回True
or(或) 有一个条件满足了就返回True
not(非) 取反
计算顺序:默认地,运算符优先级表决定了哪个运算符在别的运算符之前计算。然而,如果你想要改变它们的计算顺序,你得使用圆括号
结合规律:运算符通常由左向右结合,即具有相同优先级的运算符按照从左向右的顺序计算
'''
** #幂运算
+ - * / % #算术运算符
< > <= >= #比较运算符
== != #比较运算符
= %= /= -= += *= **= #赋值运算符
is is not #身份运算符
in not in #成员运算符
not > and > or #逻辑运算符
'''
2. 字符串
s = ' AsDasA '
s.count(x):返回字符串x在s中出现的次数,带可选参数
s.endswith(x):如果字符串s以x结尾,返回True
s.startswith(x):如果字符串s以x开头,返回True
s.find(x) :返回字符串中出现x的最左端字符的索引值,如果不在则返回-1
s.index(x):返回字符串中出现x的最左端的索引值,如果不在则抛出valueError异常
s.isalpha () :测试是否全是字母,都是字母则返回 True,否则返回 False.
s.isdigit () :测试是否全是数字,都是数字则返回 True 否则返回 False.
s.islower () :测试是否全是小写
s.isupper () :测试是否全是大写
s.lower () :将字符串转为小写
s.upper () :将字符串转为大写
s.replace (x,y) :子串替换,在字符串s中出现字符串x的任意位置都用y进行替换
s.split():返回一系列用空格分割的字符串列表
s.split(a,b):a,b为可选参数,a是将要分割的字符串,b是说明最多要分割几个
s.title() : 返回字符串,首字母大写,其它字母均小写´(Asdas)
s.rstrip() : 剔除字符串末尾空白
s.lstrip() : 剔除字符串开头的空白
s.strip() : 同时剔除字符串两端的空白
字符串拼接、格式化输出
字符串拼接
例: a = 'hello' , b = 'python' , c = '!' 将a,b ,c 中的字符串连成一句话。
第一种方法:用 + 号
a + b +c
第二种方法:格式化字符串 %s
'%s %s %s' % (a , b ,c)
第三种方法:''.join()方式,注意括号里是要连接的可以是列表,元祖
' '.join([a,b,c]) (注:''里面是连接后面各个字符串的字符)
第四种方法:.format方式
'{}{}{}'.format(a,b,c) (注:{}里面可以填入与后面相对应的符号)
format方法详解:
'{}{}{}'.format(a,b,c)
当{}里面是空的时候,里面默认索引为0,1,2按format括号里的顺序依次填入
'{1}{2}{0}'.format(a,b,c)
当{}里面有索引值时,按前面的索引值将后面的每项依次填入
'{n1}{n2}{n3}'.format(n1=a,n2=b,n3=c)
{}里面可以指定对象名称,后面通过赋值的方式给前面的相应的值,后面是无序的
格式化输出
%s 格式化字符串
%d 格式化整数
%f 格式化小数 `%1.f~%n.f`
%c 格式化ASCII字符
%o 格式化八进制
%x 格式化十六进制
%e 用科学计数法格式化
- 用作左对齐
+ 用在正数前面显示加号
m.n m是显示的最小长度,当m大于格式化位数时才起作用显示m位,n是代表小数的位数。
转义字符
\n 换行 \a提示音 \b退格键 \t横向制表符
自然字符串 r' ' # 在字符串前加上这个修饰,字符串内容将不会转义
3. 列表/元组
列表
列表由一系列按特定顺序排列的元素组成。用方括号([])来表示列表,并用逗号来分隔其中的元素。
L.append(obj) 在列表末尾添加新的对象。
L.clear() 清空整个列表。
L.copy() 复制列表。
L.count(obj) 统计某个元素在列表中出现的次数。
L.extend(obj) 用obj扩展原来的列表。
L.index(obj) 从列表中找某个值第一个匹配项的索引位置。
L.insert(index,obj) 插入元素,可以指定位置。
L.pop(index) 出栈,可以指定位置。index默认是L[-1]
L.remove(obj) 移除指定元素从左边开始的第一个。
L.reverse() 反向列表中元素。
L.sort() 对原列表进行排序。列表中的元素要类型相同 (key = len)
L.sort(reverse=True) 由大到小排序
内置函数:
sorted(L): 临时由小到大排序
sorted(L, reverse=True): 临时由大到小排序
reversed():逆序
list增删改查
查找
当你请求获取列表元素时, Python只返回该元素,而不包括方括号和引号
bicycles = ['trek', 'cannondale', 'redline', 'specialized']
print(bicycles[0])
Python为访问最后一个列表元素提供了一种特殊语法。通过将索引指定为-1,可让Python返
回最后一个列表元素:
bicycles = ['trek', 'cannondale', 'redline', 'specialized']
print(bicycles[-1])
例如,索引-2返回倒数第二个列表元素,索引-3返回倒数第三个列表元素,以此类推。
修改
修改列表元素的语法与访问列表元素的语法类似。要修改列表元素,可指定列表名和要修改
的元素的索引,再指定该元素的新值。
例如,假设有一个摩托车列表,其中的第一个元素为’honda’,如何修改它的值呢?
motorcycles = ['honda', 'yamaha', 'suzuki']
print(motorcycles)
motorcycles[0] = 'ducati'
print(motorcycles)
增加
- 在列表末尾添加元素
方法append()将元素添加到列表末尾,而不影响列表中的其他所有元素
motorcycles = ['honda', 'yamaha', 'suzuki']
print(motorcycles)
motorcycles.append('ducati')
print(motorcycles)
- 在列表中插入元素
使用方法insert()可在列表的任何位置添加新元素。为此,你需要指定新元素的索引和值。
motorcycles = ['honda', 'yamaha', 'suzuki']
motorcycles.insert(0, 'ducati')
print(motorcycles)
删除
你经常需要从列表中删除一个或多个元素。例如,当用户在你创建的Web应用中注销其账户时,你需要将该用户从活跃用户列表中删除。你可以根据位置或值来删除列表中的元素。
- 使用del语句删除元素
如果知道要删除的元素在列表中的位置,可使用del语句。
motorcycles = ['honda', 'yamaha', 'suzuki']
print(motorcycles)
del motorcycles[0]
print(motorcycles)
使用del可删除任何位置处的列表元素,条件是知道其索引。使用del语句将值从列表中删除后,你就无法再访问它了。
2. 使用方法pop()删除元素
你要将元素从列表中删除,并接着使用它的值。方法pop()可删除列表末尾的元素,并让你能够接着使用它。术语弹出(pop)源自这样的类比:列表就像一个栈,而删除列表末尾的元素相当于弹出栈顶元素。
motorcycles = ['honda', 'yamaha', 'suzuki']
print(motorcycles)
popped_motorcycle = motorcycles.pop()
print(motorcycles)
print(popped_motorcycle)
- 弹出列表中任何位置处的元素
使用pop()来删除列表中任何位置的元素,只需在括号中指定要删除的元素
的索引即可。
motorcycles = ['honda', 'yamaha', 'suzuki']
first_owned = motorcycles.pop(0)
print('The first motorcycle I owned was a ' + first_owned.title() + '.')
注意: 每当你使用pop()时,被弹出的元素就不再在列表中了。
如果你不确定该使用del语句还是pop()方法,下面是一个简单的判断标准:如果你要从列表
中删除一个元素,且不再以任何方式使用它,就使用del语句;如果你要在删除元素后还能继续
使用它,就使用方法pop()。
- 根据值删除元素
你不知道要从列表中删除的值所处的位置。如果你只知道要删除的元素的值,可使
用方法remove()。
motorcycles = ['honda', 'yamaha', 'suzuki', 'ducati']
print(motorcycles)
motorcycles.remove('ducati')
print(motorcycles)
使用remove()从列表中删除元素时,也可接着使用它的值。
motorcycles = ['honda', 'yamaha', 'suzuki', 'ducati']
print(motorcycles)
too_expensive = 'ducati'
motorcycles.remove(too_expensive)
print(motorcycles)
print("\nA " + too_expensive.title() + " is too expensive for me.")
注意 方法remove()只删除第一个指定的值。如果要删除的值可能在列表中出现多次,就需要
使用循环来判断是否删除了所有这样的值。
列表排序
- li.sort() : 对列表进行永久性正排序
- li.sort(reverse=True) : 对列表进行永久性逆排序
- sorted(li): 对列表进行临时正排序
- sorted(li,reverse=True): 对列表进行临时逆排序
- li.reverse(): 反转列表元素的排列顺序。方法reverse()永久性地修改列表元素的排列顺序,但可随时恢复到原来的排列顺序,为此只需对列表再次调用reverse()即可。
- len(li)
避免数据索引超出范围
操作列表
遍历列表
for
循环遍历列表
li = []
for i in li:
print(i)
range()
# 1. range生成列表
li = []
for i in range(1,5):
li.append(i)
print(li)
# 2. range()生成列表
li1 = list(range(1,5)) # [1, 2, 3, 4]
li2 = list(range(1,5,2)) # [1, 3]
# 3. 列表推到式
li3 = [i*2 for i in range(1,5,2)]
# 4. 迭代器
li4 = (i*2 for i in range(1,5))
# <generator object <genexpr> at 0x00000257FE4ECA20>
切片
可指定要使用的第一个元素和最后一个元素的索引。与函数range()一样,Python
在到达你指定的第二个索引前面的元素后停止。(左闭右开)
li[a,b]
:[a,b)li[:b]
:[0,b)li[a:]
:[a,len(li))/[a,-1]li[-3:]
:[-3,-1]
复制列表:
要复制列表,可创建一个包含整个列表的切片,方法是同时省略起始索引和终止索引([:])。
这让Python创建一个始于第一个元素,终止于最后一个元素的切片,即复制整个列表。
li_new = li
# 复制的li_new
是一个会跟随原列表li
变化而变化的列表。li_new = li[:]
# 复制的列表是一个全新的列表,不会受li
影响。类似于深复制和浅复制。
元组
元组看起来犹如列表,但使用圆括号而不是方括号来标识。定义元组后,就可以使用索引来
访问其元素,就像访问列表元素一样。
count(obj)统计某个元素在元组中出现的次数
index(obj)从列表中找某个值第一个匹配项的索引位置
注意:声明只有一个元素的元组时要加逗号
特点:不可变
遍历
同列表
修改元组变量
这里是修改元组变量的值,而不是元组的值。
dimensions = (200, 50)
print("Original dimensions:")
for dimension in dimensions:
print(dimension)
dimensions = (400, 100)
print("\nModified dimensions:")
for dimension in dimensions:
print(dimension)
4. 集合/字典
集合
创建:{} set([]) 注意:创建空的集合要用set()
特点:元素唯一,无序
运算: & 交集 | 并集 - 差集
方法:
s.add(x) 添加单个元素
s.update() 添加多个元素
s.remove() 移除元素
s.clear() 清空集合
字典
创建: {key:value} (大括号创建字典的键时要加引号)
dict(key=value) (括号里赋值方式,名字=对象,不要引号)
字典里的键和值用‘:’隔开,一对键和值组成一个项,项和项之间用‘,’隔开
特点:
- 键值唯一,重复会被重新复制
- 无序
添加和取值
cidt[key]=value key存在则修改值,没有则添加
属性方法
.clear() 清空字典
.copy() 复制字典
.update({ }) 在字典中添加多个项
.items() 返回字典的各个项
.keys() 返回字典的键
.values() 返回字典的值
.get(k) 如果键k在,返回键k的值,不存在则返回None
.get(k,x) 如果键k在,返回键k的值,不存在则返回x
.pop(k) 返回并移除键k所对应的元素,不存在则抛出异常
.pop(k,x) 返回并移除键k所对应的元素,不存在则返回x
.popitem(k,x) 删除一个键值对,如果没有返回为空
.setdefault(self, key, default=None, /) 插入一个键,默认值为空
总结:
key唯一,故可以是数字,字符串,元组
增删该查
dic = {‘key1’: ‘value1’, ‘key2’: ‘value2’, ‘key3’: ‘value3’}
- 添加
dic['key4'] = value4
- 修改
dic['key3'] = value5
- 删除:
del dic['key1']
- dic.pop(‘key’)
- 查找
- dic.get(‘key1’, x) :查是否有key1,没有就返回x
遍历字典
def chong():
""" 宠物信息打印 """
dog = {'type': 'dog', 'name': 'xiaohua'}
cat = {'type': 'cat', 'name': 'tangyuan'}
mouse = {'type': 'mouse', 'name': 'hong'}
pets = [dog, cat, mouse]
for i in pets:
print('宠物:')
for key, name in i.items():
print('\t',key,':',name)
1 `dic.items()`
"""
In [32]: dic = {'a':1, 'b':2}
In [33]: dic.items()
Out[33]: dict_items([('a', 1), ('b', 2)])
In [36]: print(type(a1))
<class 'dict_items'>
In [38]: print (a1)
dict_items([('a', 1), ('b', 2)])
"""
for key, value in dic.items():
print('key£:',key, 'value£:',value)
# 打印结果:
# key£: a value£: 1
# key£: b value£: 2
# 2 dic.keys()
"""
In [41]: dic.keys()
Out[41]: dict_keys(['a', 'b'])
In [42]: dic.values()
Out[42]: dict_values([1, 2])
In [44]: type(dic.keys())
Out[44]: dict_keys
In [45]: type(dic.values())
Out[45]: dict_values
"""
字典嵌套
def city_dic():
"""城市信息展示"""
cities = {
'beijing': {
'country':'CHINA',
'population':'3000w',
'fact':"2008年奥运会"
},
'wuhan': {
'country': 'CHINA',
'population':'2000w',
'fact':'2019年军运会'
},
'hangzhou':{
'country': 'CHINA',
'population':'1000w',
'fact':'2016年AEPI峰会'
},
}
for key1,value1 in cities.items():
print('城市名称:'+key1)
for key2,value2 in value1.items():
print('\t',key2,':',value2)
"""打印结果:
城市名称:beijing
country : CHINA
population : 3000w
fact : 2008年奥运会
城市名称:wuhan
country : CHINA
population : 2000w
fact : 2019年军运会
城市名称:hangzhou
country : CHINA
population : 1000w
fact : 2016年AEPI峰会
"""
5. if语句
if判断,依据条件判断最终返回的值是False或True
条件测试
# 1. 相等 ==
# 2. 不等 !=
# 3. 比较数字 age >= 18
# 4. 多条件检查
# 4.1 and
# 要检查是否两个条件都为True,可使用关键字and将两个条件测试合而为一;
# 如果每个测试都通过了,整个表达式就为True;如果至少有一个测试没有通过,
# 整个表达式就为False。
# 4.2 or
# 关键字or也能够让你检查多个条件,但只要至少有一个条件满足,就能通过整个测试。
# 仅当两个测试都没有通过时,使用or的表达式才为False。
# 5. 包含: in
# 6. 不包含: not in
# 7. 变量设置为bool值判断
if语句
if 判断语句 :
执行语句1
elif 判断语句2:
执行语句2
elif 判断语句3:
执行语句3
#...
else:
执行语句4
占位符 pass
if处理数据
# coding=utf8
import random
import json, re
def if_test1():
"""5-3 if-elif-else练习"""
alien_color_list = ['red','yellow','green']
# a = random.random() # 随机获得值
# a = random.uniform(0,2) # 0,2之间的随机数
a = random.sample(alien_color_list,1) # 列表中随机获得一个列表值
alien_color = a[0] # 获得列表中的元素
# print(alien_color)
if alien_color == 'green':
print('Player acquire 5 point')
elif alien_color == 'red':
print('Player acquire 10 point')
elif alien_color == 'yellow':
print('Player acquire 15 point')
else:
print('Not find the alient')
def if_in():
"""5-7 if in-mot in 练习"""
favorite_fruits_list = ['banana','apple','tomato','orange']
favorite_frutis = random.sample(favorite_fruits_list,1)[0]
if favorite_frutis in favorite_fruits_list:
print('You relly like :', favorite_frutis)
else:
print('请挑选你喜欢的水果!')
def user_name_judge():
"""5-10 用户注册的用户名查重判断"""
user_name = ['Ton','Tom','Arm','Json','Achjiang','Jing','Qing','pop']
new_user = ['ton','Tom','Hit','Pop','Yong','jiin']
# 方法1:
user_name_1 = str(user_name).lower()
print(user_name_1, type(user_name_1),'\n')
"""打印结果:
['ton', 'tom', 'arm', 'json', 'achjiang', 'jing', 'qing', 'pop'] <class 'str'>
"""
user_name_1 = re.findall(r'\w+', user_name_1) # 返回的是一个列表
# a = a.split(',') # 不合适再这里
print(user_name_1, type(user_name_1),'\n')
"""打印结果:
['ton', 'tom', 'arm', 'json', 'achjiang', 'jing', 'qing', 'pop'] <class 'list'>
"""
# 方法2:
# user_name_1 = []
# for j in user_name:
# j = j.lower()
# user_name_1.append(j)
# 条件判断
for i in new_user:
i = i.lower()
if i not in user_name_1:
print( i,':','is not be used !')
else:
print(i, ':', 'is be used !')
def json_test():
"""json 方法测试"""
user = {'username': 'cch', 'age': 18}
print(user)
# {'username': 'cch', 'age': 18}
a = json.dumps(user)
print(a)
# {"username": "cch", "age": 18}
if __name__ == '__main__':
# if_test1()
# if_in()
user_name_judge()
# json_test()
6. while循环
1.while循环
while 判断语句A:
执行语句B
else:
print('程序正常结束,执行else')
注意:循环要有终止条件
2.break和continue
while True:
break #终止循环
continue #跳过本次循环
#break 会终止循环,循环不再执行
#continue是跳过本次循环,循环继续
3. while-else
while True:
break
else:
print('OK')
#for
for item in iterable:
break
else:
print('OK')
"""
只有正常结束的循环,非break结束的循环才会执行else部分
"""
7. 函数
函数定义
'''
function 功能,函数
问题:一个由数字组成的列表,每次取其中两个判断大小,最后找出这个列表中最大的数
函数就是对代码进行一个封装。把实现,某一功能的相同代码,进行封装到一起。下次需要使用时,就不需要再进行代码编写,直接调用即可。
好处:
增加代码的复用性,增加代码可读性,减少代码的编写量,降低维护成本
函数可以看成,解决某类问题的 '工具'
定义方法:
'''
def function_name(params):
block
return expression(表达式)/value
'''
def 关键字表示定义一个函数
function_name 函数名,和变量名的命名要求相同,以字母和_开头,可以包含字母、数字和_
params 表示参数,可以是零个,一个 或者多个参数,函数参数不用指定参数类型
'''
函数参数
- 形参
- 实参
- 位置参数
def fun(name, age): a = fun('jiang', 16)
- 关键字参数
def fun(name, age): a = fun(name='jiang'),16
- 默认参数
def fun(name='jiang', age=18)
- 不定长参数: 可在定义时定义,也可在传入时传入
- 定义方法: def function_name(*args,**kwargs):
- *args 将传入参数包装成元组
- **kwargs将传入参数包装成字典
return
return有两个作用:1.用来返回函数的运行结果,或者调用另外一个函数。比如max()函数
2.函数结束的标志。只要运行了return,就强制结束了函数。return后面的程序都不会被执行。
如果函数中没有写return,其实函数运行结束时,默认执行了 return None
return语句的位置是可选的,不是固定出现再函数的最后,可以自定义在函数中的任何地方。
匿名函数
没有函数名的函数
g = lambda x:x+1
lambda简化了函数定义的书写形式。是代码更为简洁,但是使用函数的定义方式更为直观,易理解
lambda的应用场景:
1.有些函数如果只是临时一用,而且它的业务逻辑也很简单时,就没必要用def 来定义,这个时候就可以用lambda。
2.函数都支持函数作为参数,lambda 函数就可以应用
常见的内置函数
常见的内置函数:
查看内置函数:
print(dir(__builtins__))
常见函数
len 求长度
min 求最小值
max 求最大值
sorted 排序
reversed 反向
sum 求和
进制转换函数:
bin() 转换为二进制
oct() 转换为八进制
hex() 转换为十六进制
ord() 将字符转换成对应的ASCII码值
chr() 将ASCII码值转换成对应的字符
补充:
1.enumerate() #返回一个可以枚举的对象
2.filter() #过滤器
3.map() #加工。对于参数iterable中的每个元素都应用fuction函数,并返回一个map对象
4.zip() #将对象逐一配对
函数内变量的作用域
变量的作用域与其定义的方式有关:
局部变量: 变量在函数内部定义,则变量的作用域在函数内部
全局变量:变量在函数外部定义的,则变量的作用域是全局
global:用来在函数或其他局部作用域中,声明全局变量。 (作用于全局)
nonlocal:用来在函数或其他作用域中,声明外层(非全局)变量。(作用于局部)
使用global情况:
全局变量可以在函数内部访问,但是不能改变
如果在函数内部想修改全局变量,可以用 global 来修饰变量
局部变量只能在局部进行访问和修改。
如果在函数外部,想访问局部变量,也可以用 global,将局部变量声明为全局变量
使用nonlocal的情况:
当里层局部,需要修改外层局部时,需要使用nonlocal。 (如嵌套函数)
总结:
global :函数中,需要修改全局变量时,用global
nonlocal:当里层局部,需要修改外层局部时,需要使用nonlocal。
(局部调用局部,使用nonlocal)
内嵌函数和闭包
内嵌函数: 在函数内部定义函数,就是函数里面嵌套函数
闭包:一个闭包就是你调用了一个函数A,这个函数A返回了一个函数B给你。这个返回的函数B就叫做闭包。(但是B函数,一定要是嵌套在函数A里面) 。你在调用函数A的时候传递的参数就是自由变量。
总结:一个函数里面嵌套一个函数,调用外面这个函数,就返回里面嵌套的函数。
8. 类
创建类和实例化
创建类
# 1. python 2.7 创建类
class ClassName(object):
--snip--
pass
class Dog(object):
--snip--
pass
# 2. python3创建类
class Dog():
"""一次模拟小狗的简单尝试"""
def __init__(self, name, age):
"""初始化属性name和age"""
self.name = name
self.age = age
def sit(self):
"""模拟小狗被命令时蹲下"""
print(self.name.title() + " is now sitting.")
def roll_over(self):
"""模拟小狗被命令时打滚"""
print(self.name.title() + " rolled over!")
类实例化
- 调用属性
- 调用方法
- 修改属性
继承
如果你要编写的类是另一个现成类的特殊版本,可使用继承。一个类继承另一个类时,它将自动获得另一个类的所有属性和方法;原有的类称为父类,而新类称为子类。子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法。
# python2.7
class Car(object):
def __init__(self, make, model, year):
--snip--
class ElectricCar(Car):
def __init__(self, make, model, year):
super(ElectricCar, self).__init__(make, model, year)
--snip--
# python3
class Car():
"""一次模拟汽车的简单尝试"""
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0
def get_descriptive_name(self):
long_name = str(self.year) + ' ' + self.make + ' ' + self.model
return long_name.title()
def read_odometer(self):
print("This car has " + str(self.odometer_reading) + " miles on it.")
def update_odometer(self, mileage):
if mileage >= self.odometer_reading:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer!")
def increment_odometer(self, miles):
self.odometer_reading += miles
class ElectricCar(Car):
"""电动汽车的独特之处"""
def __init__(self, make, model, year):
"""初始化父类的属性"""
super().__init__(make, model, year)
my_tesla = ElectricCar('tesla', 'model s', 2016)
print(my_tesla.get_descriptive_name())
super()
super()是一个特殊函数,帮助Python将父类和子类关联起来。这行代码让Python调用
ElectricCar的父类的方法__init__(),让ElectricCar实例包含父类的所有属性。父类也称为超
类(superclass),名称super因此而得名。
函数super()需要两个实参:子类名和对象self。为帮助Python将父类和子类关联起来,这些
实参必不可少。另外,在Python 2.7中使用继承时,务必在定义父类时在括号内指定object。
多继承super()
继承有三种方法:
- 类名继承 #
Parent.__init__(self, name)
- super()继承 #
super().__init__(name, age, gender)
- super(具体类名, self) #
super(Grandson, self).__init__(name, age, gender)
当python3中存在多继承时,会存在使用super()调用时,不会直接调用父类的情况。主要是因为python3中在使用super()调用类的时候默认有一个
c3
算法,这个算法是用来保证每个类只被调用一次的算法。这个算法的体现是当你使用最底层类名.__mro__
打印时,会有一个(<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
类似的元组,super()调用的类按照元组的顺序进行依次调用类。
当使用
super(Son2, self).__init__(name, age, gender)
方法调用时,会直接从Son1
开始调用类,然后按照最底层类名.__mro__
中元组的顺序依次对后执行。如该部分就会跳过Son1
直接调用Son2
后的Parent
。
print("******多继承使用super().__init__ 发生的状态******")
class Parent(object):
def __init__(self, name, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('parent的init开始被调用')
self.name = name
print('parent的init结束被调用')
class Son1(Parent):
def __init__(self, name, age, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('Son1的init开始被调用')
self.age = age
super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数
print('Son1的init结束被调用')
class Son2(Parent):
def __init__(self, name, gender, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('Son2的init开始被调用')
self.gender = gender
super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数
print('Son2的init结束被调用')
class Grandson(Son1, Son2):
def __init__(self, name, age, gender):
print('Grandson的init开始被调用')
# 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍
# 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
# super(Grandson, self).__init__(name, age, gender)
super().__init__(name, age, gender)
print('Grandson的init结束被调用')
print(Grandson.__mro__)
gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)
print("******多继承使用super().__init__ 发生的状态******\n\n")
运行结果:
******多继承使用super().__init__ 发生的状态******
(<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
Grandson的init开始被调用
Son1的init开始被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
性别: 男
******多继承使用super().__init__ 发生的状态******
总结:
- super().__init__相对于类名.init,在单继承上用法基本无差
- 但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,具体看前面的输出结果
- 多继承时,使用super方法,对父类的传参数,应该是由于python中super的算法导致的原因,必须把参数全部传递,否则会报错
- 单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错
- 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍, 而使用super方法,只需写一句话便执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
面试题
class Parent(object):
x = 1
class Child1(Parent):
pass
class Child2(Parent):
pass
print(Parent.x, Child1.x, Child2.x)
Child1.x = 2
print(Parent.x, Child1.x, Child2.x)
Parent.x = 3
print(Parent.x, Child1.x, Child2.x)
print(Child2.__mro__)
# 打印结果
"""
1 1 1
1 2 1
3 2 3
(<class '__main__.Child2'>, <class '__main__.Parent'>, <class 'object'>)
"""
说明:
使你困惑或是惊奇的是关于最后一行的输出是 3 2 3 而不是 3 2 1。为什么改变了 Parent.x 的值还会改变 Child2.x 的值,但是同时 Child1.x 值却没有改变?
这个答案的关键是,在 Python 中,类变量在内部是作为字典处理的。如果一个变量的名字没有在当前类的字典中发现,将搜索祖先类(比如父类)直到被引用的变量名被找到(如果这个被引用的变量名既没有在自己所在的类又没有在祖先类中找到,会引发一个 AttributeError 异常 )。
因此,在父类中设置 x = 1 会使得类变量 x 在引用该类和其任何子类中的值为 1。这就是因为第一个 print 语句的输出是 1 1 1。
随后,如果任何它的子类重写了该值(例如,我们执行语句 Child1.x = 2),然后,该值仅仅在子类中被改变。这就是为什么第二个 print 语句的输出是 1 2 1。
最后,如果该值在父类中被改变(例如,我们执行语句 Parent.x = 3),这个改变会影响到任何未重写该值的子类当中的值(在这个示例中被影响的子类是 Child2)。这就是为什么第三个 print 输出是 3 2 3。
定义子类属性方法
重写父类方法
示例属性化
classmethod
、staticmethod
和property
方法说明
classmethod
、staticmethod
方法
方法包括:实例方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
-
实例方法:由对象调用;至少一个self参数;执行实例方法时,自动将调用该方法的对象赋值给self;
-
类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类赋值给cls;
-
静态方法:由类调用;无默认参数;
classmethod
:是一个类方法staticmethod
:
静态方法是在定义一个供当前类调用,但是又不想传递参数(self-实例参数/cls-类参数)的情况下产生的一种方法,这个方法只能被这个类调用,其它类不能调用。使用示例如下:
class Foo(object):
# 类属性
country = '中国'
def __init__(self, name):
# 实例属性
self.name = name
def ord_func(self):
""" 定义实例方法,至少有一个self参数 """
# print(self.name)
print('实例方法')
@classmethod
def class_func(cls):
""" 定义类方法,至少有一个cls参数 """
print('类方法')
@staticmethod
def static_func():
""" 定义静态方法 ,无默认参数"""
print('静态方法')
# 1.属性:
# 创建一个实例对象
obj = Foo('山东省')
# 直接访问实例属性
print(obj.name)
# 直接访问类属性
Foo.country
# 2.方法:
f = Foo("中国")
# 调用实例方法
f.ord_func()
# 调用类方法
Foo.class_func()
# 调用静态方法
Foo.static_func()
实例属性和类属性对比:
- 类属性在内存中只保存一份
- 实例属性在每个对象中都要保存一份
方法对比:
- 相同点:对于所有的方法而言,均属于类,所以 在内存中也只保存一份
- 不同点:方法调用者不同、调用方法时自动传入的参数不同。
property
方法
Python的property属性的功能是:property属性内部进行一系列的逻辑计算,最终将计算结果返回。
property
方法有两种用法,一种是使用装饰器的方式对函数进行装饰,一种是使用类属性的方法。
- 装饰器 即:在方法上应用装饰器
- 类属性 即:在类中定义值为property对象的类属性
装饰器
Python中的类有经典类和新式类,新式类的属性比经典类的属性丰富。( 如果类继object,那么该类是新式类 )
经典类,具有一种@property装饰器
# ############### 定义 ###############
class Goods:
@property
def price(self):
return "laowang"
# ############### 调用 ###############
obj = Goods()
result = obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
print(result)
新式类,具有三种@property装饰器
#coding=utf-8
# ############### 定义 ###############
class Goods:
"""python3中默认继承object类
以python2、3执行此程序的结果不同,因为只有在python3中才有@xxx.setter @xxx.deleter
"""
@property
def price(self):
print('@property')
@price.setter
def price(self, value):
print('@price.setter')
@price.deleter
def price(self):
print('@price.deleter')
# ############### 调用 ###############
obj = Goods()
obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
obj.price = 123 # 自动执行 @price.setter 修饰的 price 方法,并将 123 赋值给方法的参数
del obj.price # 自动执行 @price.deleter 修饰的 price 方法
注意
- 经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
- 新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法
由于新式类中具有三种访问方式,我们可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
类属性方式,创建值为property对象的类属性
当使用类属性的方式创建property属性时,经典类和新式类无区别
class Foo:
def get_bar(self):
return 'laowang'
BAR = property(get_bar)
obj = Foo()
reuslt = obj.BAR # 自动调用get_bar方法,并获取方法的返回值
print(reuslt)
property方法中有个四个参数
第一个参数是方法名,调用 对象.属性 时自动触发执行方法
第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
第四个参数是字符串,调用 对象.属性.doc ,此参数是该属性的描述信息
#coding=utf-8
class Foo(object):
def get_bar(self):
print("getter...")
return 'laowang'
def set_bar(self, value):
"""必须两个参数"""
print("setter...")
return 'set value' + value
def del_bar(self):
print("deleter...")
return 'laowang'
BAR = property(get_bar, set_bar, del_bar, "description...")
obj = Foo()
obj.BAR # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = "alex" # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入
desc = Foo.BAR.__doc__ # 自动获取第四个参数中设置的值:description...
print(desc)
del obj.BAR # 自动调用第三个参数中定义的方法:del_bar方法
由于类属性方式创建property属性具有3种访问方式,我们可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
综上所述:
定义property属性共有两种方式,分别是【装饰器】和【类属性】,而【装饰器】方式针对经典类和新式类又有所不同。
通过使用property属性,能够简化调用者在获取数据的流程。其中python2中适合装饰器的经典类和类属性,不适合装饰器的新式类;python3都适合。
9. 文件异常处理
文件
#1.打开文件 open 函数
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
# file 是要打开的文件
# encoding 默认编码方式为空,一般window系统的需要设置'UTF-8'
# mode 是打开文件的方式,默认为r--读
#2.文件的打开模式
r 只读模式,文件不存在时会报错。
w 写入模式,文件存在会清空之前的内容,文件不存在则会新建文件。
x 写入模式,文件存在会报错,文件不存在则会新建文件。
a 追加写入模式,不清空之前的文件,直接将写入的内容添加到后面。
b 以二进制模式读写文件,wb,rb,ab。
+ 可读写模式,r+,w+,x+,a+,这几种模式还遵循了r,w,x,a的基本原则。
#3.文件的读取
f.read(size) #读取文件的内容,将文件的内容以字符串形式返回。
'''size是可选的数值,指定字符串长度,如果没有指定size或者指定为负数,就会读取并返回整个文件。当文件大小为当前机器内存两倍时就会产生问题,反之就尽可能大的size读取和返回数据,如果到了文件末尾,会返回空字符串。
'''
f.readline() #从文件中读取单独一行。
'''字符串结尾会自动加上一个换行符\n,只有当文件最后没有以换行符结尾时,这一操作才会被忽略,这样返回值就不会有混淆。如果返回空字符串,表示到达率文件末尾,如果是空行,就会描述为\n,一个只有换行符的字符串。
'''
f.readlines() #一次读取所有,返回一个列表,列表的元素为文件行的内容。
'''可以通过列表索引的方式将文件的每一行的内容输出。
可以通过for循环迭代输出每一行的信息。
'''
#4.文件的写入
f.write() #将要写入的内容以字符串的形式通过write方法写入文件中。
f.writelines() #括号里必须是由字符串元素组成的序列。
#5.保存和关闭
f.flush()
#在读写模式下,当写完的数据想要读取出来时,要先缓存区的内容保存到文件当中。
f.close()
#关闭文件。对一个已经关闭的文件进行操作会报错。
#6.光标位置
f.tell() #返回光标在文件中的位置。
f.seek(offset,from) #常用 f.seek(0) 返回起始位置
#在文件中移动文件指针,从from(0代表起始位置,1代表当前位置,2代表文件末尾)偏移offset个字节。
# 常用 f.seek(0,0) 返回起始位置。 其他了解一下
#7.查看文件信息
closed #查看文件是否已经关闭,返回布尔值。
mode #返回文件打开模式。
name #返回文件名。
#8.with 形式打开文件,里面的语句执行完后会自动关闭文件
with open('文件名') as f:
f.read()
读文件
# coding=utf-8
# 1 Windows读取文件
# Windows环境路径,`\`非转义字符,所以前面加r不识别,避免报语法错误
file_path = r"C:\Users\luo\Desktop\123.txt"
with open(file_path, encoding='UTF-8') as f:
content = f.read()
# print(type(f), f)
# <class '_io.TextIOWrapper'> <_io.TextIOWrapper name='C:\\Users\\luo\\Desktop\\123.txt' mode='r' encoding='UTF-8'>
print('----------', '\n', content)
with open() as f:
好处:
关键字with在不再需要访问文件后将其关闭。在这个程序中,注意到我们调用了open(),但
没有调用close();你也可以调用open()和close()来打开和关闭文件,但这样做时,如果程序存
在bug,导致close()语句未执行,文件将不会关闭。这看似微不足道,但未妥善地关闭文件可能
会导致数据丢失或受损。如果在程序中过早地调用close(),你会发现需要使用文件时它已关闭
(无法访问),这会导致更多的错误。并非在任何情况下都能轻松确定关闭文件的恰当时机,但通
过使用前面所示的结构,可让Python去确定:你只管打开文件,并在需要时使用它, Python自会
在合适的时候自动将其关闭。
文件路径
- 在Linux和OS X中,你可以这样编写代码:
file_path = '/home/ehmatthes/other_files/text_files/filename.txt'
with open(file_path) as file_object:
- 在Windows系统中,在文件路径中使用反斜杠(\)而不是斜杠(/):
file_path = r'C:\Users\ehmatthes\other_files\text_files\filename.txt'
with open(file_path) as file_object:
注意 Windows系统有时能够正确地解读文件路径中的斜杠。如果你使用的是Windows系统,且
结果不符合预期,请确保在文件路径中使用的是反斜杠。
- 文件读取函数
- f.read() : 读取整个文件 <class ‘str’>
- f.readline() : 读取第一行文件,返回list
- f.readlines() : 读取所有行文件, 回list
# 2 Windows环境下读取列表
file_list_path = r"C:\Users\luo\Desktop\文档\test.txt"
p_str = ''
with open(file_list_path, encoding='UTF-8') as f:
content = f.read() # 读取文档 <class 'str'>
# 打印内容
"""
[1,2,3,4,5,6]
[11,12,13,14,15,16]
[2,21,22,23,24,25]
"""
# content = f.readline() # 只读第一行,返回列表
# [1,2,3,4,5,6]
# content = f.readlines() # 读所有行,返回列表
# ['[1,2,3,4,5,6]\n', '[11,12,13,14,15,16]\n', '[2,21,22,23,24,25]']
# for line in content:
# print(line.rstrip(), type(line)) # rstrip()方法去除字符串首尾的空格, <class 'str'>
# p_str += line
"""
[1,2,3,4,5,6]
[11,12,13,14,15,16]
[2,21,22,23,24,25]
"""
print('----------', '\n', type(content), content) # <class 'list'>
# print('----------', '\n', type(p_str), p_str) # <class 'str'>
写文件
注意 Python只能将字符串写入文本文件。要将数值数据存储到文本文件中,必须先使用函数
str()将其转换为字符串格式。
- 写文件主要有以下几种方式:
- 读取模式(‘r’)、
- 写入模式(‘w’)、
- 附加模式(‘a’)
- 够读取和写入文件的模式(‘r+’)
- 写入空文件:
# 写空文件
filename = 'programming.txt'
with open(filename, 'w') as f:
f.write("I love programming.")
注意: 如果你要写入的文件不存在,函数open()将自动创建它。然而,以写入(‘w’)模式打开文
件时千万要小心,因为如果指定的文件已经存在, Python将在返回文件对象前清空该文件。
- 写多行文件
filename = 'programming.txt'
with open(filename, 'w') as f:
f.write("I love programming.\n")
f.write("I love creating new games.\n")
- 写附加文件
- 如果你要给文件添加内容,而不是覆盖原有的内容,可以附加模式打开文件。
- 你以附加模式打开文件时,Python不会在返回文件对象前清空文件,而你写入到文件的行都将添加到文件末尾。
- 如果指定的文件不存在, Python将为你创建一个空文件。
filename = 'programming.txt'
with open(filename, 'a') as f:
f.write("I also love finding meaning in large datasets.\n")
f.write("I love creating apps that can run in a browser.\n")
存储数据
模块json让你能够将简单的Python数据结构转储到文件中,并在程序再次运行时加载该文件
中的数据。你还可以使用json在Python程序之间分享数据。更重要的是,JSON数据格式并非Python
专用的,这让你能够将以JSON格式存储的数据与使用其他编程语言的人分享。这是一种轻便格
式,很有用,也易于学习
json.dump()和 json.load()
- json.dump()来存储这组数字
import json
username = input("What is your name? ")
filename = 'username.json'
with open(filename, 'w') as f_obj:
json.dump(username, f_obj)
print("We'll remember you when you come back, " + username + "!")
- json.load()加载存储信息
import json
def greet_user():
""""""
filename = 'username.json'
try:
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
username = input("What is your name? ")
with open(filename, 'w') as f_obj:
json.dump(username, f_obj)
print("We'll remember you when you come back, " + username + "!")
else:
print("Welcome back, " + username + "!")
greet_user()
保存和读取用户数据
import json
# 如果以前存储了用户名,就加载它
# 否则,就提示用户输入用户名并存储它
filename = 'username.json'
try:
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
username = input("What is your name? ")
with open(filename, 'w') as f_obj:
json.dump(username, f_obj)
print("We'll remember you when you come back, " + username + "!")
else:
print("Welcome back, " + username + "!")
重构
代码能够正确地运行,但可做进一步的改进——将代码划分为一系列完成具体工作的函数。这样的过程被称为重构。重构让代码更清晰、更易于理解、更容易扩展。
import json
def get_stored_username():
"""Èç¹û´æ´¢ÁËÓû§Ãû£¬¾Í»ñÈ¡Ëü"""
filename = 'username.json'
try:
with open(filename) as f_obj:
username = json.load(f_obj)
except FileNotFoundError:
return None
else:
return username
def greet_user():
"""ÎʺòÓû§£¬²¢Ö¸³öÆäÃû×Ö"""
username = get_stored_username()
if username:
print("Welcome back, " + username + "!")
else:
username = input("What is your name? ")
filename = 'username.json'
wi
异常处理
try:
suite1 #测试语句块
except exception1:
suite2 #如果测试语句suite1中发生exception1异常时执行
except (exception2,exception3):
suite3 #如果测试语句suite1中发生元组中任意异常时执行
except exception4 as reason: #as把异常的原因赋值给reason
suite4 #如果测试语句suite1发生exception4的异常时执行
except:
suite5 #如果测试语句suite1发生异常在所列出的异常之外时执行
else:
suite5 #如果测试语句块suite1中没有发生异常时执行
finally:
suit6 #不管测试语句suite1中又没有发生异常都会执行
'''
注意:中间的except,else,finally都是可选的,但至少有一个,不然try就没有意义了,根据实际中的需求来选择。
所有错误类型都继承自BaseException
https://docs.python.org/3/library/exceptions.html#exception-hierarchy
'''
#注意:如果抛出父类异常,在子类不会再获取,如下:
try:
fun()
except Exception as e:
raise Exception
except ImportError as e:
raise ImportError
finally:
pass
'''
在上面的例子中,下面的ImportError就不会被抛出,应为ImportError继承Exception,但是可以把Exception放在后面是可以的
e可以得到系统给出的报错信息
'''
assert #断言
'''
测试表示式,其返回值为假,就会触发异常
'''
assert 1==1 #没有返回值
assert 1==2 #返回AssertionError