pycharm常用快捷键
python是一个弱语言编码,在变量声明的时候对数据类型不是很严格。围绕变量从三个方向学习:1.变量命名;2.变量赋值;3.数据类型。 创建变量的基本格式:变量名=变量
变量名的命名规范:
1.字母、数字、下划线(不能数字开头、不能使用关键字、严格区分大小写)。
2.每个单词大写GetName、get_name或用下划线间隔。
变量赋值不同于C和Java不需要声明变量类,系统会自动进行数据类型的判断。感觉很奈斯!
一般情况下 整数会被认为是int型
小数会被认为是float型
单双三引号会被认为是字符串
赋值为true和false会被认为是布尔类型
然后会根据数据的大小长度再细分为是long型、double型。
无须定义,就很nice! 字符串的赋值 字符串:‘单引号’、“双引号”、‘’‘三引号’‘’
单双引号用于在输出时避免编译出错:’xx说“xxxx”‘
三引号用于保留格式输出:
a=‘’’
123
456
789
‘’’
则输出样式与你三引号中的样式一样。 字符串格式化 :引用%s、%d等来替换输出字符串中的变量。
name = 'Tom'
age = 18
# 输出语句
print("my name is " + name + ". I'm " + str(age) + " years odl.")
# 这样的输出格式和Java有点相像
# 输出语句格式化
print("my name is %s. I'm %d years odl" % (name, age))
# 这样的输出格式和C有点相像
重名变量:变量可以重名,在函数使用时,会就近原则,会使用函数之前离函数最近的变量进行使用。
基本数据类型:int、float、string、boolean。type() 用于判断数据类型。
类型转换:
例:
a=1
type(a) 单独使用没有输出
格式:type(变量名) 判断变量类型,用print输出
print(type(变量名))
print(type(a)) 用print辅助输出
input(builtins 内置标准库) 和 print
用input输入的值在输出时都会被封装成字符串类型,!!!注意:这时候再对输入的内容进行修改就需要注意数据类型适当运用数据类型转换。
字符串与字符串的相加就等于字符串的拼接。
格式:变量名=input(‘输入处的指导提示信息’)
小区别:在c语言中输入的变量类型是可以控制的,而且有时候还会帮助你转换,但python中输入的就是字符串,而且它还不帮你转。
print使用时是默认换行的,
print()的end参数使用总结:
print(list,end=”“)
1.end的默认参数是换行:end=”\n“
2.end设置为空字符可以阻断换行:end”“
3.end可以设置任意字符串:end=”++“
字符串转整形可以,但带小数点的字符串不能转整形;
one = input('输入第一个数:')
two = input('输入第二个数:')
print(one + two)
# 字符串的拼接
print(int(one) + int(two))
# 字符转换成整形
three = input('输入第三个数:')
four = input('输入第四个数:')
print(three + four)
# 字符串的拼接
print(int(three) + int(four))
# 字符转换成整形
字符串转浮点型;
int —> str //整形转字符串;
float —> str //浮点型转字符串;
int —> float //整形转浮点型;
float —> int //浮点型转整形:小数点之后的数会被去掉。
boolean —> int //布尔型转整形:True---->1
False---->0
boolean —> float //布尔型转浮点型:True---->1.0
False---->0.0
boolean —> str //布尔型转字符串:True---->‘True’
False---->’False‘
int —> boolean //整形转布尔类型:大于0的数----->True
空字符串、 0----->False
id函数:用来查看变量的地址 is函数:用来比较多个变量的存储地址是否相同
#id函数的用法
a='abcd'
print(id(a))
#------------结果
12345678
#is函数的用法
b='1234'
c=b
print(b is c)
#------------结果
True
+、-、*、/
= ---> 赋值
+=、-=
*= ---> 自乘
**= ---> 次方运算
/= ---> 自除
//= ---> 整除
%= ---> 模运算(求余)
> 、<、 ==、 !=、 >=、 <=
and 与
or 或
not 非
& ---> 按位与运算
| ---> 按位或运算
^ ---> 按位异或运算
~ ---> 按位非运算
<< ---> 按位左移
>> ---> 按位右移
用于不固定次数的循环 格式: while 条件: #条件成立才运行(布尔值为True)
循环体 #循环体要注意循环结束条件
else: 上面的循环体没有出现中断过(break)就会执行下面的指令 # 注:else不仅可以放在while循环中,for循环同样适用,下面for循环中用法一样。
执行指令
break:跳出当前循环结构
例:
count=0
while True:
print(count)
count+=1
if count==n
break #跳出循环
可以用if配合break来在某一种情况下跳出循环,相比起直接用while,while要明确一个主要的循环条件或循环次数,
if配合break就可以针对运算中的某一种小类情况进行跳出,或是不明确循环条件时。
有固定次数的循环 格式:
for 变量名 in range(n):
print(变量名)
打印 n-0个数,默认从0开始 循环 n-1次
for 变量名 in range(start,stop): #包前不包后
print(变量名)
打印 stop-start个数,从start开始 循环 stop-start次
for 变量名 in range(start,stop,step):
print(变量名)
step:步数值:每step次打印(循环)出的变量的变化量由1变为step
for i in range(n):
循环体
else:
执行指令
for i in range(1, 11):
print(i, end="")
print()
for i in range(0, 11):
print(i, end="")
print()
for i in range(11):
print(i, end="++")
for i in range(0, 11, 2):
print(i)
s='abcd12345'
for i in s:
print(i) #结果:---> a b c ... 1 2 ....
代码执行结果: 区别for循环的几种用法和熟悉print输出的几种方式
continue语句:
在循环中出现continue,则跳过之后的循环内容,从新循环。
跳过本次循环(后面的语句不执行)继续下一次循环。
区别于break:
在循环中出现break,则跳出循环体执行后面的语句。
循环嵌套 —> 打印三角 例:
n = 1
while n <= 5:
print('*' * n) # 这里的格式是python中特有的
n += 1
print('------------------------')
n = 1
while n <= 5:
m = 0
while m < n:
print('*', end='')
m += 1
n += 1
print()
(运算符构成条件)
格式: if 条件:
条件成立执行的语句
例:
d=input(‘请输入id:/n’) #提示:这里的input输入的是字符串类型,所以在下面判断的时候要进行转换 不然就 if id==‘1’
if id==int(1):
print(你的id为1)
print(。。。。。)
。。。。
python又一大特点:
python中不用{}来规定内容范围,python用缩进来表示该行代码是否与上一行是一体的。if条件语句中,if条件命令下的条件命令就是if命令行下缩进在if之后的命令语句
格式1:
if 条件1:
条件1成立执行的语句
else:
条件1不成立执行的语句
格式2:
if 条件1:
条件1成立执行的语句
elif 条件2:
条件2成立执行的语句
elif 条件3:
条件3成立执行的语句
.....
else:
以上条件都不成立执行的语句
if的简写形式:
成立的执行语句 if 条件 else 不成立的执行语句,比如:print(ok) if a>b else print(no) 例:
a = int(input('输入比大小的a值:'))
b = int(input('输入比大小的b值:'))
print('Ok') if a > b else print('No')
if会将条件结果自动转换成一个布尔值:
只有在空字符串和0、None、()、{}、[]的时候,布尔值为False,其他时候都为True
随机数:
import random #导入random这个库
ran=random.randint(范围) #random.randint(范围:1,10):生成随机数的函数
绝对值:
abs(变量) #将变量变为绝对值
在python中有一个字符串保留区,当中存放了声明的字符串,每一个字符串对应一个地址,变量名指向对应的地址,
只要字符串相同,声明内容相同的字符串变量,他都指向同一个地址。
'''
例:
s1 = 'hello'
s2 = s1
s3 = 'hello'
s4 = 'hello1'
---> 其中 s1、s2、s3 这三个变量的地址都相同
----> 具体解释: s2 = s1 的实质就是将 s1的地址赋给s2
s3 = 'hello' 因为在字符串保留区中已经存在了 hello 这个字符串,所以为了节省内存,就直接使用原来字符串的地址。
s4 = 'hello1' 这个字符串因为还未出现在字符串保留区,所以它会有一个自己的地址,而不是用’老地址‘
'''
#代码
s1 = 'hello'
s2 = s1
s3 = 'hello'
s4 = 'hello1'
print(id(s1))
print(id(s2))
print(id(s3))
print(id(s4))
一个字符串,每一个字符都对应了一个编号。
例: A B C D E F G H I
index: 0 1 2 3 4 5 6 7 8 --->这是其中一种编号方式从左自右,从0开始
index: -9 -8 -7 -6 -5 -4 -3 -2 -1 --->这是另一种编号方式从右自左,从-1开始
1、 0~len(s)-1
2、 -len(s)~ -1
虽然两种编号方式不同,但是在切片时,都是从左自右的书写,并且两种编号方式可以混合用
例:
#截取(结果为某一字符)
s1 = 'ABCDEFG'
print(s1[4]) ------> 结果:E
print(s1[0]) ------> 结果:A
print(s1[-1]) ------> 结果:G
print(s[1:4]) ------> 结果:BCD (规则:左闭右开,左取右不取)
print(s[0:5]) ------> 结果:ABCDE
print(s[:5]) ------> 结果:ABCDE (从0到index=4,包左不包右)
print(s[-3:-1]) ------> 结果:EF (还是先写左再写右)
print(s[-3:7]) ------> 结果:EFG (两种编号可以混合使用)
print(s[-3:]) ------> 结果:EFG (从-3开始到结尾)
print(s[:]) ------> 结果:ABCDEFG (相当于 print(s),且两个变量的地址一样)
x = s[:]
print(x)
print(s) --->结果相同
print(id(x))
print(id(s)) --->结果相同
字符串变量[start: end :step] 默认是一个一个取这个元素,加入步长后,每次取 当前位置+步长(step)
print(s[:-1:2]) ------> 结果:ACE
print(s[1::2]) ------> 结果: BDF
print(s[::4]) ------> 结果: AE
step:
1.步长
2.方向 step 正数 从左向右
step 负数 从右向左
print(s[::-1]) ------> 结果: GFEDCBA (默认从头开始,从右段开始,方向看步长的正负)
print(s[::]) ------> 结果: ABCDEFG (默认从头开始,从左段开始)
print(s[0:6:-2]) ------> 错误示范
原因:它从0开始,步数为负,方向向左,则无法到index=6
print(s[6:0:-2]) ------> 结果:GECA
path = 'https: // www.bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999'
print(len(path)) # --->结果:71
path = 'https: // www.bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999'
# 例-find
i = path.find('w')
j = path.find('m')
print(path[i:j + 1]) # --->结果:www.bilibili.com
# 除了单个字符的查找,字符串也可以
i = path.find('bilibili')
print(i) # 多个字符时它会返回字符串的第一个字符的位置
# --->结果:14
print(path[i:]) # --->结果:bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999
# 例-rfind
a = path.rfind('i')
print(path[a:]) # --->结果:id_from = 333.999
# 例-index
# b = path.index('$')
# print(b)
# rindex不做演示
path = 'https: // www.bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999'
print(path.count('b')) # --->结果:2
path = 'https: // www.bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999'
print(path.replace('bilibili', 'dilidili')) # replace('被替换内容',‘添加进去的内容’,个数),
切割字符串之后,切割的内容都会被放在列表中。
path = 'https: // www.bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999'
kuai = '''
aaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbb
cccccccccccccccccc
dddddddddddddddddd
'''
print(path.split('/', 1)) # --->结果:['https: ', '/ www.bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999']
print(path.rsplit('/', 1)) # --->结果:['https: // www.bilibili.com / video ', ' BV1R7411F7JV?spm_id_from = 333.999']
print(kuai.splitlines()) # --->结果:['', 'aaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbb', 'cccccccccccccccccc', 'dddddddddddddddddd']
print(path.partition('/')) # --->结果:('https: ', '/', '/ www.bilibili.com / video / BV1R7411F7JV?spm_id_from = 333.999')
print(
path.rpartition('/')) # --->结果:('https: // www.bilibili.com / video ', '/', ' BV1R7411F7JV?spm_id_from = 333.999')
KPL = 'hello World'
print(KPL.capitalize()) # --->结果: Hello world
print(KPL.title()) # --->结果:Hello World
print(KPL.upper()) # --->结果: HELLO WORLD
print(KPL.lower()) # --->结果:hello world
KK = 'wwwbilibili com'
print(KK.ljust(30))
print(KK.rjust(30))
KK = KK.center(30)
print(KK)
print(KK.lstrip())
print(KK.rstrip())
print(KK.strip())
name = 'orange'
age = 18
# 格式化符号
print("my name is %s. I'm %d years odl" % (name, age))
#format格式化
print("my name is {}. I'm {} years odl".format(name, age))
print("my name is {0}. I'm {1} years odl.I want to eat {0}".format(name, age))
print("my name is {name}. I'm {age} years odl".format(name='orange', age=18))
#--->结果:my name is orange. I'm 18 years odl
#--->结果:my name is orange. I'm 18 years odl.I want to eat orange
用于存储多个元素 ,列表也是一种数据类型,所以列表可以套列表(可借用数据结构中广义表的概念) ,列表元素的索引方式和字符串的方式相同(下标、切片)。
list1['abc','bcd','efg'] list[1,2,3,4,5,1.5]
#列表可以套列表:
list3=[['a'],['b','c'],['d','e','f']]
定义列表
list1 = []
list2 = ['苹果']
list3 = ['牛奶', '面包']
list1.append('酸奶')
print(list1) # 结果:--->['酸奶']
list2.append('酸奶')
print(list2) # 结果:--->['苹果', '酸奶']
list3.append(list2)
print(list3) # 结果:--->['牛奶', '面包', ['苹果', '酸奶']]
list1.extend(list2)
print(list1) # 结果:--->['酸奶', '苹果', '酸奶']
list2.extend(list3)
print(list2) # 结果:--->['苹果', '酸奶', '牛奶', '面包', [...]]
print(list1 + list1) # 结果:--->['酸奶', '苹果', '酸奶', '酸奶', '苹果', '酸奶']
一个嵌套小练习
# 例:超时购物
container = [] # 存放商品
flag = True
while flag:
name = input('请输入商品名:')
price = input('请输入价格:')
number = input('请输入个数:')
goods = [name, price, number]
container.append(goods)
answer = input('是否继续购买(按q或Q退出)')
if answer.lower() == 'q':
flag = False
for goods in container:
print(goods)
# 例:pop
alist = [1, 2, 3, 4, 5, 6, 7, 8, 9]
alist.pop(4)
print(alist) # 结果:--->[1, 2, 3, 4, 6, 7, 8, 9]
# 例:remove
blist = [1, 0, 4, 2, 3, 5, 3, 2, 4, 4, 5, 5]
blist.remove(5)
print(blist) # 结果:--->[1, 0, 4, 2, 3, 3, 2, 4, 4, 5, 5]
# 易错事例
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
for i in a:
if i == '酸奶':
a.remove(i)
print(a) # 结果:--->['火腿肠', '辣条', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
# 重新遍历的方法,可以避免这个问题
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
for i in a[::]:
if i == '酸奶':
a.remove(i)
print(a) # 结果:--->['火腿肠', '辣条', '面包', '蛋糕', '可乐']
# 也可以用取元素的方法创建一个新列表
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
b = []
for i in a:
if i != '酸奶':
b.append(i)
print(b)
# 也可以先查看列表中待删元素的个数来决定删除的重复次数
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
b = a.count('酸奶')
for i in range(b):
a.remove('酸奶')
print(a)
# 例:clear
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
a.clear()
print(a)
# 例:del
# del删除指定下标元素
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
del a[0]
print(a) # 结果:--->['辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
# del删除列表名
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
b = a
del a
print(b) # 结果:--->['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
# print(a) # 结果:--->报错
insert:在某个位置插入一个值,其他元素往后移
格式:a.insert(位置,新元素) 找到位置,加入值
append:插入一个元素放在最后,这也是与insert的区别
# 例:insert
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
a.insert(0, '奶茶')
print(a) # 结果:--->['奶茶', '火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
# 例:append
a.append('奶茶')
print(a) # 结果:--->['奶茶', '火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶', '奶茶']
# 例:extend
a = [1, 1, 1, 1, 1]
b = [2, 2, 2, 2, 2]
a.extend(b)
print(a) # 结果:--->[1, 1, 1, 1, 1, 2, 2, 2, 2, 2]
b.extend(a)
print(b) # 结果:--->[2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2]
元素 in 列表 —> 返回布尔类型
与之相对的是 not in
列表.index(元素) —> 返回元素的下标位置,如果没有此元素则报错
列表.count(元素) —> 返回整数,如果元素不存在则返回0,如果元素存在则返回元素个数
# 例:元素 in 列表
a = ['a', 'b', 'c', 'd']
if 'a' in a:
print(True)
else:
print(False)
# 结果:--->True
if 'h' not in a:
print(True)
else:
print(False)
# 结果:--->True
# 例:index
print(a.index('a')) # 结果:--->0
# 例:count
a = ['火腿肠', '辣条', '酸奶', '酸奶', '面包', '蛋糕', '可乐', '酸奶', '酸奶']
print(a.count('酸奶')) # 结果:--->4
# 例:sort
a = [12, 43, 31, 76, 42, 98, 14, 35, 57]
a.sort() # 默认情况下 reverse=False
print(a) # 结果:--->[12, 14, 31, 35, 42, 43, 57, 76, 98]
a = [12, 43, 31, 76, 42, 98, 14, 35, 57]
a.sort(reverse=True)
print(a) # 结果:--->[98, 76, 57, 43, 42, 35, 31, 14, 12]
# 例:数值交换
a = 111
b = 222
print(id(a), id(b)) # 结果:--->2567481677616 2567481681232
a, b = b, a
print(a, b) # 结果:--->222 111
print(id(a), id(b)) # 结果:--->2567481681232 2567481677616
# 可以理解成改变了变量名指向的地址
一种简化表达式来得到列表。
格式1:[i for i in 可迭代的]:把循环结构放入到列表中。
for循环用于产生列表中的元素i,在将i放入列表中
格式2:[i for i in 可迭代的 if条件]:将循环和判断一同放入列表中。
先进行循环,拿到i;再用i来判断是否要存入列表中
格式3:[i if 条件 else 命令 for i in 可迭代的]:如果存在有条件不成立要执行的语句,就用到if …else…
把if…else…放在循环前面。先循环拿参数i;把i带入判断语句进行判断,如果判断为True,存入列表;如果判断为False,执行命令。
格式4:[(i,j)for i in 可迭代的 for j in 可迭代的]:多个for循环,靠前的for先执行
#格式一:
list1 = []
for i in range(1, 21):
list1.append(i)
print(list1) # 结果:--->[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
list1 = [i for i in range(1, 21)]
print(list1) # 结果:--->[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
list1 = [i for i in range(0, 51, 2)]
print(
list1) # 结果:--->[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50]
list1 = [i * 2 for i in range(0, 26)]
print(
list1) # 结果:--->[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50]
#格式二:
list1 = [i for i in range(0, 51, 2) if i % 2 == 0]
print(
list1) # 结果:--->[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50]
#格式三:
list2 = ['abc', 'eFg', 'hij', 'KLM', 'NOp', 'Qr']
list1 = [i if i.isupper() else i.upper() for i in list2]
print(list1) # 结果:--->['ABC', 'EFG', 'HIJ', 'KLM', 'NOP', 'QR']
#格式四:
list1 = [(i, j) for i in range(1, 4) for j in range(1, 4)]
print(list1) # 结果:--->[(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3)]
# 小练习-按要求取元素
a = ['123', '456', 'apple', 'age', 'Faker', '927', 'hello']
list1 = [i for i in a if i.isalpha()]
print(list1) # 结果:--->['apple', 'age', 'Faker', 'hello']
list1 = [i for i in a if i.isdigit()]
print(list1) # 结果:--->['123', '456', '927']
元组和列表相似,但是元组中的元素不能修改仅供查看。元组使用小括号:();列表使用方括号[]。tuple :表示元组; list :表示列表
注意:只有一个元素的元组的定义和多个元组定义的不同
# 例:定义元组
# 空元组
a = ()
print(type(a)) # 结果:--->
print(a) # 结果:--->()
# 单个元素的元组
b = ('aa',) # 结果:--->('aa',),单个元素必须在元素后面加逗号
c = ('a', 'b', '1', '2') # 结果:--->('a', 'b', '1', '2')
print(b)
print(c)
元组中的元素也对应了下标,可以通过下标来查找对应位置的元素,也可以通过下标切片:tuple[1:3:-1]。
# 例:下标与切片
a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
print(a[0]) # 结果:--->1
print(a[1:4]) # 结果:--->(2, 3, 4)
print(a[::-1]) # 结果:--->(0, 9, 8, 7, 6, 5, 4, 3, 2, 1)
index:找到对应位置的下标
格式:a=tuple.index(元素,起始位置,结束位置),index都可以这么用
count:计数,统计元组中的元素个数
len:求长度
in, not in
for i in tuple:
while
# 例:操作
# index
n = a.index(3)
print(n) # 结果:--->2
# count
n = a.count(3)
print(n) # 结果:--->1
# len
n = len(a)
print(n) # 结果:--->10
# in
if 1 in a:
print(True) # 结果:--->True
# for
for i in a:
print(i) # 结果:--->1,2,3,4,5,6,7,8,9,0
用类型转换,将元组强制转换成列表,通过修改列表来增删改,改完在把列表转换成元组。
# 例:修改元组
b = list(a)
b.append('a')
a = tuple(b)
print(a) # 结果:--->(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 'a')
概念理解:将元组拆解成一个一个的元素,这些元素可以单独装包给一些变量,这是可以一个元素装一个变量,也可以多个元素装一个变量。拆包这个不仅可以在元组上使用,列表、字典都可以。
拆包格式:变量a,变量b…=元组1/列表1/字典1
拆包注意事项:变量设置的个数要与元组/列表/字典中的元素个数要对应,,过多过少都不好。
数据类型的改变
通过拆包分出来的元素,他们的数据类型都会改变。在拆包、装包时用通配符会将元素都会变成列表类型。在输出时使用通配符也会将其原有数据类型的特征去掉。
# 例:元组拆包与装包
a = ('1', '2', '3', '4', '5', '6', '7')
b, c, d, *e = a
print(b, c, d) # 结果:--->1 2 3
print(type(b)) # 结果:--->
print(e) # 结果:--->['4', '5', '6', '7']
print(type(e)) # 结果:--->
print(*a) # 结果:--->1 2 3 4 5 6 7
# 例:字典拆包与装包
a = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
q, w, *e = a
print(q) # 结果:--->a
print(type(q)) # 结果:--->
print(e) # 结果:--->['c', 'd', 'e']
print(*e) # 结果:--->c d e
print(type(e)) # 结果:--->
print(*a) # 结果:--->a b c d e
# 例:列表拆包与装包
a = [1, 2, 3, 4, 5, 6, 7, 8]
g, h, j, *f = a
print(g) # 结果:--->1
print(type(g)) # 结果:--->
print(f) # 结果:--->[4, 5, 6, 7, 8]
print(type(f)) # 结果:--->
print(*a) # 结果:--->1 2 3 4 5 6 7 8
注意输出方式与输出结果,理解通配符的作用以及拆包与装包操作。
字典定义要求成对:key:value(键:值)
# 例:定义空列表
dict1 = {}
print(dict1) # 结果:--->{}
dict2 = dict()
print(dict2) # 结果:--->{}
# list1=list(),tuple1=tuple()是一样的
注意:key和value要对应,这样才能定义成功。key是唯一的,value是可变的。在定义的时候key不能对应多个value。
#错误示范
classmate={'name':'Tom','name':'Tony'}
print(classmate) # 结果:---->报错
上述代码是不行的,你的一个key不能有两个value值。好比一个人的身份证号码是唯一的,可以通过身份证号找到你叫张三,当然别人也可以叫张三,甚至叫李四。字典类比身份证,身份证号码类比key值,名字类比value值。
# 例:定义非空列表
zhangsan = {'age': 18, 'sex': '男'}
print(zhangsan) # 结果:--->{'age': 18, 'sex': '男'}
重点讲解方式二
用方式二定义字典需要使用列表,元组,在这个过程中先进行一个拆包和装包,他把列表、元组拆装重组成字典,因为列表中的元组有一对元素,所以才能将其对应的放入key和value中。
默认将元组中的前者放入key中,后者放入value中,单独使用列表不行,他会将列表中的元素都当作key值,单独使用元组也不行。只有列表中的元素成对存在时才能定义成功。
# 例:定义列表二
kuangtu = dict([('name', 'zhangsan'), ('age', '28'), ('sex', 'man')])
print(kuangtu) # 结果:--->{'name': 'zhangsan', 'age': '28', 'sex': 'man'}
#特殊示范--元组套列表
A = dict((['name', 'xiaohong'], ['age', 20], ['sex', 'man']))
print(A) # 结果:--->{'name': 'xiaohong', 'age': 20, 'sex': 'man'}
# 错误示范--只使用列表
xiaoming = dict(['age', '18', 'sex', 'man'])
print(xiaoming) # 结果:--->报错
# 错误示范--只是要元组
daming = dict(('age', '18', 'sex', 'man'))
print(daming) # 结果:--->报错
在创建字典时,对字典内元素的添加要求是成对添加,只使用列表和只使用元组会无法确定key值和value值。第二个特殊示范用元组套列表,只要对应号key,value还是能成功的,但我在pycharm中编写的时候显示意外类型,这代表这样的操作并不常用,但是合理。
格式:字典名[key1]=value1
这样就将key1和value1配对起来存入字典中了
注意:当你在同样个字典中用同一个key存入一个新value时,会进行覆盖
# 例:字典的覆盖
dict1 = {}
dict1['name'] = 'Tom'
dict1['name'] = 'Tony'
print(dict1) # 结果:--->{'name': 'Tony'}
key值是唯一的,value值可以是不唯一的。
# 小练习-利用字典存储
print('----------字典存储----------')
database = [] # 这个列表用来存储字典,把他放在循环外以免被置空
while True:
name = input('姓名:') #两年半练习生、时间管理带师、加拿大电音王
age = input('年龄:') #18、38、28
sex = input('性别:') #男、男、男
user = {} # 每一次都会在这儿创建一个空字典
user['name'] = name
user['age'] = age
user['sex'] = sex
database.append(user)
Flag = input('是否继续?(w退出)')
if Flag == 'w':
break
print(database)
# 结果:--->[{'name': '两年半练习生', 'age': '18', 'sex': '男'}, {'name': '时间管理带师', 'age': '38', 'sex': '男'}, {'name': '加拿大电音王', 'age': '28', 'sex': '男'}]
字典中找元素根据key来找,取元素也是。切记如果字典中找不到输入的key值,则会报错。可以去看字典中的函数 —> get
for循环遍历
for i in dict: —> 把每一个key都依次赋给i
可用 in。单独遍历字典的结果是:字典的key
# 例:单个查询
a = {'name': 'kali', 'dream': 'fly', 'talent': 'computer'}
print(a['name']) # 结果:--->kali
# 借用上述字典循环遍历
for i in a: # in把字典a中的key依次拿出来给到i
print(a[i], end="/") # 结果:--->kali/fly/computer/
print(i, end="/") # 结果:--->dream/computer/talent/
取出所有的key值和value值,将字典中key和对应的value转换成一个元组,并放入列表中
# 例:字典中的函数-items
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
b = a.items()
c = dict.items(a)
print(b) # 结果:--->dict_items([('name', 'hacker'), ('love', 'kali'), ('talent', 'computer')])
print(c) # 结果:--->dict_items([('name', 'hacker'), ('love', 'kali'), ('talent', 'computer')])
# 两种格式都可以。print(a.items()),print(dict.items(a))也是一样的
用这个函数可对value进行查询操作
# 例:字典中的函数-items-对value进行查询
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
for key, value in a.items():
if value == 'kali':
print(key) # 结果:--->love
# 通过value值查找key值
# 遍历直接得到value值
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
for key, value in a.items():
print(value, end='/') # 结果:--->hacker/kali/computer/
此时的dict.items()是一个列表–[(),(),(),()],关键字in每次都将列表中的一个元组拿出赋给 key,value 元组–(key1,value1),元组中正好也是成对的元素,这就可以分别赋给两个。
只取出字典中所有value值。
# 例:字典中的函数-values
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
print(a.values()) # 结果:--->dict_values(['hacker', 'kali', 'computer'])
b = dict.values(a)
print(b) # 结果:--->dict_values(['hacker', 'kali', 'computer'])
只取出字典中所有key。
# 例:字典中的函数-keys
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
print(a.keys()) # 结果:--->dict_keys(['name', 'love', 'talent'])
b = dict.keys(a)
print(b) # 结果:--->dict_keys(['name', 'love', 'talent'])
不仅在列表,元组中可以用,字典也可以
用key去找value,比起直接用key去寻找,他不会报错,并且他可以设默认数据—0,‘none’,‘这里没有你要的东西’
# 例:字典中的函数-get
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
print(a.get('name')) # 结果:--->hacker
# 对比get函数和通过key值直接查询
print(a.get('大帅逼', '字典中没有发现改key')) # 结果:--->字典中没有发现改key
print(a['大帅逼']) # 结果:--->报错
格式:del dict[‘key’]
如果没有key值,则会报错
# 例:字典中的函数-get
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
del a['name']
print(a) # 结果:--->{'love': 'kali', 'talent': 'computer'}
del a['大帅逼']
print(a) # 结果:--->报错
# 因为没有没有key值,所以报错
del a
print(a) # 结果:--->报错
# 因为这时候a这个字典已经被删除了,没有字典a,所以打印会报错
就删除了指定的key
格式:dict.pop(‘key’,默认数据)
result=dict.pop(‘key’,默认数据)
如果key存在则能删除成功,且result会得到被删除的key的对应value值
如果key不存在则删除失败,result会得到这个默认数据
# 例:删除函数-pop
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
result = a.pop('大帅逼', '该字典中没有该key')
print(a) # 结果:--->{'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
print(result) # 结果:--->该字典中没有该key
result = a.pop('love', '该字典中没有该key')
print(a) # 结果:--->{'name': 'hacker', 'talent': 'computer'}
print(result) # 结果:--->kali
# 当使用pop函数不添加默认数据时,他也会报错
a.pop('大帅逼')
print(a) # 结果:--->报错
随机删除字典中的键值对,不用指定key值,一般是从末尾删除元素---->结合下面随机概念理解。
随机概念:字典是没有下标与编号的,所有可以理解为字典中的键值对是乱序的
格式:dict.popitem()
# 例:删除函数-popitem
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
a.popitem()
print(a) # 结果:--->{'name': 'hacker', 'love': 'kali'}
dict.popitem(a)
print(a) # 结果:--->{'name': 'hacker'}
a.popitem()
print(a) # 结果:--->{}
可以根据上述结果确定popitem函数每次都是删除最后一个元素。
清空字典
# 例:删除函数-clear
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
a.clear()
print(a) # 结果:--->{}
字典与字典的相加
格式:dict1.update(dict2)
字典中相同key的会进行value值的覆盖,没有的key会进行追加
# 例:其他内置函数-update
a = {'name': 'hacker', 'love': 'kali', 'talent': 'computer'}
b = {'name': 'kali', 'dream': 'fly', 'talent': 'computer'}
a.update(b) # 这里是a调用update函数,所以是在字典a中进行覆盖与追加
print(a) # 结果:--->{'name': 'kali', 'love': 'kali', 'talent': 'computer', 'dream': 'fly'}
print(b) # 结果:--->{'name': 'kali', 'dream': 'fly', 'talent': 'computer'}
将字符串、列表、元组转换成字典,前面这几个数据类型中的元素会被存储为key值,默认是将value值置为none,也可以自己设定默认值。
# 例:其他内置函数-fromkeys
a = [1, 2, 3, 'si', 'wu']
b = ('kali', 'hacker', 'computer', 4, 5)
c = dict.fromkeys(a)
print(c) # 结果:--->{1: None, 2: None, 3: None, 'si': None, 'wu': None}
d = dict.fromkeys(b, 666)
print(d) # 结果:--->{'kali': 666, 'hacker': 666, 'computer': 666, 4: 666, 5: 666}
e = dict.fromkeys(b, a)
print(e)
# 结果:--->{'kali': [1, 2, 3, 'si', 'wu'], 'hacker': [1, 2, 3, 'si', 'wu'], 'computer': [1, 2, 3, 'si', 'wu'], 4: [1, 2, 3, 'si', 'wu'], 5: [1, 2, 3, 'si', 'wu']}
没有重复的元素,元素的排列是无序的(没有下标),但会自动从小到大排列数字,如果是一个字符串(字符)和数字混合的集合,输出集合时也会将数字进行排列。
集合也用花括号声明定义({})。
定义空集合:set1=set()
空集合的定义只能这样,因为集合和字典定义用到括号一样,如果用:set1={}会被认为是定义的字典
定义非空集合:set2={‘xiaoxiaoguai’}
用花括号定义,如果括号中的是单个元素,则是集合,括号中的是一对元素,则是字典。
# 例:定义集合
# 空集合
a = set()
print(type(a)) # 结果:--->
# 虽然集合也是用{}表示,但是定义空集合时不能使用
# 错误示范
b = {}
print(type(b)) # 结果:--->
# 定义非空集合
a = set('关于我学习python结果完不成任务这件事') # 只能设置一个参数,不能用逗号多设置参数。这样会把长字符串中每一个字符都作为集合中的一个元素。
print(a) # 结果:--->{'任', '成', '关', '完', 'o', '务', '结', '不', 't', 'n', '习', '我', '事', '件', '这', 'p', '于', 'y', '学', '果', 'h'}
b = {'大大怪', '小小怪', '猪猪侠'} # 可设置多个参数,多个参数的设置用逗号隔开
print(b) # 结果:--->{'大大怪', '小小怪', '猪猪侠'}
c = {0, 1, 9, 2, 8, 3, 7, 4, 6, 5, 5, 6, 6, 4, 4, 4} # 集合特点展示
print(c) # 结果:--->{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
# 例:添加元素
a = set()
b = {'we', 'are', 'family'}
print(a) # 结果:--->
print(b) # 结果:--->
a.add('boy')
a.add('girl')
a.add('father') # add也只能带一个参数,不能同时添加多个元素
a.add('mother')
a.add('girl') # 重复添加,但是输出结果只会有一个'girl'
a.add('sister')
print(a) # 结果:--->{'sister', 'girl', 'boy', 'father', 'mother'}
b.add(a) # add只能添加元素,不能添加其他类型的数据(列表、元组、字典)
print(b) # 结果:--->报错
b.update(a)#注意不要把追加对象弄反了
print(a) # 结果:--->{'sister', 'girl', 'boy', 'father', 'mother'}
print(b) # 结果:--->{'mother', 'sister', 'girl', 'boy', 'father', 'family', 'we', 'are'}
# 例:移除元素-remove
a = {'mother', 'sister', 'girl', 'boy', 'father', 'family', 'we', 'are'}
a.remove('boy') # 正常演示
print(a) # 结果:--->{'father', 'girl', 'sister', 'family', 'are', 'we', 'mother'}
b = a.remove('sister') # 看删除时是否会赋值
print(b) # 结果:--->None :从结果上是会有一个返回值的,none
print(a) # 结果:--->{'father', 'girl', 'family', 'are', 'we', 'mother'}
a.remove('a') # 移除不存在的元素,看是否有报错
print(a) # 结果:--->报错 :有报错
c = a.remove('b') # 看赋值的结果是否与集合内的元素有关
print(c) # 结果:--->报错 :实际上返回的话也会是none,这一点可以用discar这个去看出来
# 例:移除元素-discard
a = {'mother', 'sister', 'girl', 'boy', 'father', 'family', 'we', 'are'}
a.discard('boy') # 正常演示
print(a) # 结果:--->{'father', 'girl', 'sister', 'family', 'are', 'we', 'mother'}
b = a.discard('sister') # 看删除时是否会赋值
print(b) # 结果:--->None :从结果上是会有一个返回值的,none
print(a) # 结果:--->{'father', 'girl', 'family', 'are', 'we', 'mother'}
a.discard('a') # 移除不存在的元素,看是否有报错
print(a) # 结果:--->{'father', 'girl', 'family', 'are', 'we', 'mother'}
c = a.discard('b') # 看赋值的结果是否与集合内的元素有关
print(c) # 结果:--->None :其实discard和remove是一样的,从这里就能看出携带值是无关于集合中的内容
# 例:移除元素-clear
a = {'mother', 'sister', 'girl', 'boy', 'father', 'family', 'we', 'are'}
b = a.clear()
print(a) # 结果:--->set()
print(b) # 结果:--->None
# 删除后还会剩下一个空集合,还是可以往集合a中存入元素
# 例:移除元素-pop
a = {'mother', 'sister', 'girl', 'boy', 'father', 'family', 'we', 'are'}
a.pop()
print(a) # 结果:--->{'father', 'boy', 'mother', 'family', 'we', 'sister', 'are'}
b = a.pop()
print(a) # 结果:--->{'sister', 'father', 'we', 'boy', 'family', 'are'}
print(b) # 结果:--->mother
# 例:移除元素-del
a = {'mother', 'sister', 'girl', 'boy', 'father', 'family', 'we', 'are'}
del a
print(a) # 结果:--->报错 :直接报错,且报错原有是未定义‘a’这个变量
# del相比clear,del是直接把集合去除,而不是把集合中的所有元素去除。
# 例:集合中的交并差
a = {1, 2, 3, 4, 5}
b = {3, 4, 5, 6, 7}
c = {1, 3, 5, 7, 9}
# intersection-交集
print(a.intersection(b)) # 结果:--->{3, 4, 5}
# 交集的符号化表示运算
print(a & b) # 结果:--->{3, 4, 5}
# union-并集
print(b.union(c)) # 结果:--->{1, 3, 4, 5, 6, 7, 9}
# 并集的符号化表示运算
print(b | c) # 结果:--->{1, 3, 4, 5, 6, 7, 9}
# difference-差集
d = b.difference(c)
print(d) # 结果:--->{4, 6}
# 差集的符号化表示运算
e = b - c
print(e) # 结果:--->{4, 6}
# 集合的概念和数学中集合的概念差不多,然后交集、并集、差集,这些也和数学中的概念相识。
# 符号化是更简便的一种方式,并且这些符号和逻辑符有些相识,可以与逻辑符结合记忆理解。
结合操作方法(感谢菜鸟教程)
方法 | 描述 |
---|---|
add() | 为集合添加元素 |
clear() | 移除集合中的所有元素 |
copy() | 拷贝一个集合 |
difference() | 返回多个集合的差集 |
difference_update() | 移除集合中的元素,该元素在指定的集合也存在。 |
discard() | 删除集合中指定的元素 |
intersection() | 返回集合的交集 |
intersection_update() | 返回集合的交集。 |
isdisjoint() | 判断两个集合是否包含相同的元素,如果没有返回 True,否则返回 False。 |
issubset() | 判断指定集合是否为该方法参数集合的子集。 |
issuperset() | 判断该方法的参数集合是否为指定集合的子集 |
pop() | 随机移除元素 |
remove() | 移除指定元素 |
symmetric_difference() | 返回两个集合中不重复的元素集合。 |
symmetric_difference_update() | 移除当前集合中在另外一个指定集合相同的元素,并将另外一个指定集合中不同的元素插入到当前集合中。 |
union() | 返回两个集合的并集 |
update() | 给集合添加元素 |
小练习
#产生五组不重复的六位随机码
import random
data = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890'
J = set()
while True:
code = ''
for i in range(6):
r = random.choice(data) # random.randint-->使用的是下标;random.choice-->使用的是字符串
code = code + r
J.add(code)
if len(J) == 5:
break
print(J) # 结果:-->{'IKIXFw', '9T39WR', 'wpTXtK', '3bQbDC', 'Lx84e9'}
此时,python中的数据类型就基本学完了。其中有基本编程语言都会有的基础数据类型,也有python中存在的列表、元组、字典、集合。基础数据类型较为熟悉,这里就把列表、元组、字典、集合这四个数据类型做个小总结:
# 类型转换-list
# list--->tuple
a = ['a', 1, 'b', 2, 'c', 3]
b = tuple(a)
print(b) # 结果:--->('a', 1, 'b', 2, 'c', 3)
# list--->set
print(set(a)) # 结果:--->{'a', 1, 2, 3, 'c', 'b'}
# 特殊转换:list--->dict
a = [['a', 'b'], ('c', 1), (4, 'd'), ['3', 2]]
print(dict(a)) # 结果:--->{'a': 'b', 'c': 1, 4: 'd', '3': 2}
# 类型转换-tuple
# tuple--->list
a = ('a', 1, 'b', 2, 'c', 3)
b = list(a)
print(b) # 结果:--->['a', 1, 'b', 2, 'c', 3]
# tuple--->set
print(set(a)) # 结果:--->{'b', 1, 2, 'c', 3, 'a'}
# 特殊转换:tuple--->dict
a = (['a', 'b'], ('c', 1), (4, 'd'), ['3', 2])
print(dict(a)) # 结果:--->{'a': 'b', 'c': 1, 4: 'd', '3': 2}
# 类型转换-set
# set--->list
a = {'a', 1, 'b', 2, 'c', 3}
b = list(a)
print(b) # 结果:--->[1, 2, 3, 'b', 'c', 'a']
# set--->tuple
print(tuple(a)) # 结果:--->(1, 2, 3, 'b', 'c', 'a')
# 类型转换-dict
# dict--->list
a = {'a': 'k', 1: 9, '5': 'b', 'p': 2, 'c': 3}
b = list(a)
print(b) # 结果:--->['a', 1, '5', 'p', 'c']
# dict--->tuple
print(tuple(a)) # 结果:--->('a', 1, '5', 'p', 'c')
# 只能提出key值
print(list(a.items())) # 结果:--->[('a', 'k'), (1, 9), ('5', 'b'), ('p', 2), ('c', 3)]
print(tuple(a.items())) # 结果:--->(('a', 'k'), (1, 9), ('5', 'b'), ('p', 2), ('c', 3))
# 使用dict的方法就能取出键值对
# 内置方法-bin
a = 12345
b = bin(a)
print(b) # 结果:--->0b11000000111001
# 0b表示二进制,之后的才是转换成的二进制数
# 内置方法-oct
a = 12345
print(oct(a)) # 结果:--->0o30071
# 0o表示该数是八进制,之后的才是转换成的八进制数
# 内置方法-hex
a = 12345
print(hex(a)) # 结果:--->0x3039
# 0x表示该数是十六进制,之后的才是转换成的十六进制数
# 内置方法-chr
a = 86
print(chr(a)) # 结果:--->V
# 一次转换一个,可以用循环来转换多个
# 内置方法-ord
a = 'A'
print(ord(a)) # 结果:--->65
# 一次转换一个,可以用循环来转换多个
# 内置方法-max
a = [31, 35, 46, 236.9, 98, 3, 54, 79, 75]
print(max(a)) # 结果:--->236.9
# 求列表中的最大值,浮点数,整数都能求,列表,元组,集合都能用
# 内置方法-min
a = (31, 35, 46, 236.9, 98, 3, 54, 79, 75, 0.5)
print(min(a)) # 结果:--->0.5
# 内置方法-sum
a = {31, 35, 46, 236.9, 98, 3, 54, 79, 75, 0.5}
print(sum(a)) # 结果:--->658.4
# 内置方法-abs
a = -123456
print(abs(a)) # 结果:--->123456
# 内置方法-sorted
a = [31, 53, 57, 79, 47, 13, 97]
print(sorted(a)) # 结果:--->[13, 31, 47, 53, 57, 79, 97]
a = (31, 53, 57, 79, 47, 13, 97)
print(sorted(a, reverse=True)) # 结果:--->[97, 79, 57, 53, 47, 31, 13]
# 运算符--'+'
# 字符串
a = 'Cynthia'
b = 'beautiful'
print(a + b) # 结果:--->Cynthiabeautiful
# 列表
a = ['m', 'o', 'o', 'n']
b = ['l', 'o', 'v', 'e']
print(a + b) # 结果:--->['m', 'o', 'o', 'n', 'l', 'o', 'v', 'e']
# 元组
a = ('m', 'o', 'o', 'n')
b = ('l', 'o', 'v', 'e')
print(a + b) # 结果:--->('m', 'o', 'o', 'n', 'l', 'o', 'v', 'e')
# 运算符--'*'
# 字符串
a = 'Cynthia'
print(a * 2) # 结果:--->CynthiaCynthia
# 列表
a = ['m', 'o', 'o', 'n']
b = ['l', 'o', 'v', 'e']
print((a, b) * 2) # 结果:--->(['m', 'o', 'o', 'n'], ['l', 'o', 'v', 'e'], ['m', 'o', 'o', 'n'], ['l', 'o', 'v', 'e'])
# 元组
a = ('C', 'y', 'n', 't', 'h', 'i', 'a')
b = ('b', 'e', 'a', 'u', 't', 'i', 'f', 'u', 'l')
print([a,b] * 2)
# 结果:--->[('C', 'y', 'n', 't', 'h', 'i', 'a'), ('b', 'e', 'a', 'u', 't', 'i', 'f', 'u', 'l'), ('C', 'y', 'n', 't', 'h', 'i', 'a'), ('b', 'e', 'a', 'u', 't', 'i', 'f', 'u', 'l')]
# 运算符--'in'
# 字符串
a = 'Cynthia'
print('l' in a) # 结果:--->False
# 列表
a = ['m', 'o', 'o', 'n']
print('o' in a) # 结果:--->True
# 元组
b = ('l', 'o', 'v', 'e')
print('v' in b) # 结果:--->True
# 字典
b = {1: 2, 3: 4, 5: 6, 7: 8, 9: 0}
print(1 in b) # 结果:--->True
# 运算符--'not in'
# 字符串
a = 'Cynthia'
print('l' not in a) # 结果:--->True
# 列表
a = ['m', 'o', 'o', 'n']
print('o' not in a) # 结果:--->False
# 元组
b = ('l', 'o', 'v', 'e')
print('v' not in b) # 结果:--->False
# 字典
b = {1: 2, 3: 4, 5: 6, 7: 8, 9: 0}
print(1 not in b) # 结果:--->False
函数主要就是对功能性操作打包,方便复用。在需要时直接调用就可以实现这个功能。
#格式:def 函数名 ([参数]):
# 执行代码
注意:当识别到def时,系统会给这个函数分配一个地址,并且开辟一个空间来存储这个函数的代码。此时函数名就代表代表该函数被分配的地址,函数名():这才代表调用该函数。
# 小练习-生成随机码的函数
import random
# 无参函数
def generate_code():
source = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890'
code = ''
for i in range(6):
r = random.choice(source)
code += r
print(code)
# 传参函数
def generate_code1(n):
source = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890'
code = ''
for i in range(n):
r = random.choice(source)
code += r
print(code)
generate_code() # 结果:--->16Y92Q 生成一个六位随机码
generate_code1(4) # 结果:--->7TYf 生成一个四位随机码
print(generate_code) # 结果:--->
print(generate_code1) # 结果:--->
#直接打印函数名,返回的结果是函数的地址,这一点很重要**名字常表示一个地址**
函数可以带参数也可以不带参数,所以从参数的角度可以把函数分为两类:无参函数、有参函数(单个参数或多个参数)。
格式:
无参函数:def 函数名():
函数体
有参函数:def 函数名(参数1,参数2,参数3…):
函数体
注意:如果函数设置了参数,那么在调用函数时就要传入对应的参数。
# 例:有参函数的定义
def sum(a, b, c, d):
return a + b + c + d
r = sum(2, 5, 9, 10)
print(r) # 结果:--->26
w = sum('l', 'i', 'k', 'e')
print(w) # 结果:--->like
# 错误示范
e = sum(2, 5, 9)
print(e) # 结果:--->报错
# 在传入参数时,参数要与定义时的参数设置要对应,类型不强制要求,但带入函数体中要求能正常进行,否则也报错
# 例:默认参数
def age(a, b=18):
print('{}今年{}岁'.format(a, b))
age('boy') # 结果:--->boy今年18岁
age('tony', 22) # 结果:--->tony今年22岁
这里调用函数并没有给参数b赋值,但是打印结果依然有值返回,这就是默认函数的特点,在定义时设置的默认值参数可在调用时传入新的参数,如果不传新参就返回默认值,传就返回传入值。这样就就避免了有些参数不输入就会报错的问题。同样,默认参数在使用时也有一些需要注意的点。
# 注意一:默认参数的位置
def print_age(b=18, c='单身',a):
print('{}今年{}岁还是{}。'.format(a, b, c))
print_age('张三') # 结果:--->报错
报错的原因就是在定义函数时将默认参数设置在了普通参数前.
# 注意二:多个默认参数的赋值
def print_age(a, b=18, c='单身', d='本地'):
print('{}是{}人'.format(a, d))
print('{}今年{}岁还是{}。'.format(a, b, c))
print_age('张三', '四川', 30, '成功人士') # 结果:--->张三是成功人士人\n张三今年四川岁还是30。
print_age(a='张三', b=30, c='成功人士', d='四川') # 结果:--->张三是四川人\n张三今年30岁还是成功人士。
这样的结果是因为在有多个默认参数时进行赋值,如果不指明具体参数,它会根据定义函数时的默认顺序去带入函数。如果遇到这样的情况,就用关键字传参来避免位置的不对应。
# 可变参数---*
def print_number(*number):
print(number)
print('--------')
print(*number)
print_number(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
# 结果:--->(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
# 1 2 3 4 5 6 7 8 9 0
print_number(111)
# 结果:--->(111,)
# 111
# 利用可变函数做加法运算
def get_sum(*number):
s = 0
for i in number:
s += i
return s
a = get_sum(1, 2, 3, 4, 5, 6, 7, 8, 9)
print(a)
# 可变参数---**
def show_book(**book):
print(book)
print(*book)
show_book(bookname='斗破苍穹', author='天蚕土豆', number=10)
# 结果:--->{'bookname': '斗破苍穹', 'author': '天蚕土豆', 'number': 10}
# bookname author number
返回值与传参是对应的关系,参数是用于像函数中传入参数,而返回值则是函数中将值传出。有返回值的函数在调用时需要有一个变量进行接收。
# 返回值:return
def b(*ff):
return ff
j = a(1, 2)
print(j) # 结果:--->(1, 2)
j,r=a(1,2)
print(j,r) # 结果:--->1 2
o, u, y = b(11, 22, 33, 44, 55, 66, 77, 88, 99)
print(o, u, y)#结果:--->报错
o, u, *y = b(11, 22, 33, 44, 55, 66, 77, 88, 99)
print(o, u, y) # 结果:--->11 22 [33, 44, 55, 66, 77, 88, 99]
注意:返回值是可以返回单个和多个返回值的。在接收返回值时可以选择用一个变量进行接收,这样的话多个返回值会被一起放入到一个元组中;当然也可以设置多个变量进行接收,需要注意的是,如果用多个变量一一接收,这要求变量和返回值的个数对应,否则会报错。在接收时也可以用一个星号去接收,可避免设置多个变量的麻烦。单独使用return可以起到终止程序进行的作用。
函数中的就叫局部变量(临时保存,函数被创建调用时存在,函数执行结束后局部变量也被回收),程序中设置的变量就叫全局变量。
# 全局变量和局部变量
def get_add(number):
number *= 2
if number > 10:
login = True
def TF():
if login == True:
print('number*2>10')
else:
print('number*2<10')
login = False
a = int(input('input number:')) # 输入:---> input number:10
get_add(a)
TF() # 结果:---> number*2<10
上面就是全局变量和局部变量出现的一个问题。当有全局变量且在函数中要修改全局变量时,一般情况下是无法修改的。只能调用不能修改。
在函数中调用变量的机制:在函数中如果要调用某一变量,系统会先从函数内部去查看是否有该变量名的局部变量,如果没有,则会去函数外找是否有对应的全局变量,之后如果也没在程序中找到有对应的全局变量,系统就会在自己的系统中查看是否有对应的系统变量。以上都没有发现对应变量,则程序报错。
在函数调用变量的过程中,如果发现变量是全局变量,则无法对其进行修改(部分变量除外),若要对其进行改动,则需要使用关键字global。
# 添加了global
def get_add(number):
number *= 2
if number > 10:
global login
login = True
def TF():
if login == True:
print('number*2>10')
else:
print('number*2<10')
login = False
a = int(input('input number:')) # 输入:---> input number:10
get_add(a)
TF() # 结果:---> number*2>10
print(globals())
# 结果:--->{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000002BC0C9937C0>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'E:\\寒假-python爬虫\\python-基础语法\\草稿.py', '__cached__': None, 'get_add': , 'TF': , 'login': True, 'a': 11}
globlas函数查看全局变量:在python中这个函数可以用于查看全局变量。函数所返回的全局变量包括了系统内置的全局变量和自定义的全局变量两种。
global的使用:不是所有数据类型的变量都需要global去进行全局变量的改动,是否加global取决于数据类型是可变还是不可变。
# 可变与不可变
def a(*a):
for i in a:
list1.append(i)
print(list1)
def b(*b):
for i in b:
list1.append(i)
print(list1)
list1 = []
a(1, 2, 3, 4, 5, 6) # 结果:--->[1, 2, 3, 4, 5, 6]
b('a', 'b', 'c', 'd') # 结果:--->[1, 2, 3, 4, 5, 6, 'a', 'b', 'c', 'd']
上述例子中,在函数中修改了数据类型为列表的全局变量但并没有使用global,因为global属于可变类型。
对于数据类型可变与不可变可以这样理解,这些不可变数据类型地址是唯一的。以整形数据为例,整形每一个数都指向一个唯一确定的地址,如果在函数中想要进行修改,那么一定会改变该变量的地址指向,没有global就无法去修改该地址指向。可变型数据的地址虽然也是确定唯一的,但是在进行修改的时候它的地址是不会改变的。(列表、字典、集合相当于一个容器,用于存储数据,在对该类数据进行修改操作时,它是对容器中的数据进行操作,但本身的地址是没有改变的)
在sys库中,sys.getrefcount()可用来查看变量引用次数(指向该值的指针个数)。值传递的过程就是变量的引用(a=b,赋值传递;id(a),函数调用,等)。某些值在python的系统中本身就被引用了很多次,如a=0这种小的数就被引用了很多次。
变量引用的意义:变量引用的意义在于理解值的传递。在python中,值的传递实质上是地址的传递,变量名是方便我们使用的数据,变量值是系统所处理的数据两者之间靠指针相连,如果变量值是相同的,一般它的地址也是相同的,这时可能会有多个不同变量名指向这同一个地址。(更好的理解全局变量与局部变量的可变与不可变)
# 引用
import sys
a = 5892
b = a
c = a
print(id(a)) # 结果:--->2201458870416
print(id(b)) # 结果:--->2201458870416
print(id(c)) # 结果:--->2201458870416
r = sys.getrefcount(a)
print(r) # 结果:--->6
a = 9999
print(id(a)) # 结果:--->2201458870480
print(id(b)) # 结果:--->2201458870416
print(id(c)) # 结果:--->2201458870416
r = sys.getrefcount(a)
print(r) # 结果:--->4
可以看出,b,c在从a那儿获取到地址之后,即使a改变了地址,b,c的地址也没有发生改变。
参数的引用和变量的引用是相同的。
# 参数的引用
def b(k):
k += 1
m = 9
b(m)
print(m) # 结果:--->9
#9的地址是唯一的,且m指向9这个地址,函数中传入的k是一个临时变量运算结果为10,指向的是另一个地址。
#打印m,m的地址指向并没有发生改变,所有打印结果还是9
def a(l):
l.insert(0, 0)
list1 = [1, 2, 3, 4]
a(list1)
print(list1) # 结果:--->[0, 1, 2, 3, 4]
#list1的地址一直都没有发生改变,变的是该地址中所存储的值
引用总结:
函数的嵌套在函数的使用中非常重要,这可以实现函数功能的完备性,也是装饰器的基础。
# 简单的嵌套
def name():
print('my name is a.')
def age():
print('I am 18 years old.')
print(locals())
return age()
name()
# 结果:--->my name is a.
# 结果:--->{'age': .age at 0x000001E8A436CAF0>}
# 结果:--->I am 18 years old
# 嵌套函数的参数传递
def number(*args):
s = 0
for i in args:
s += i
def add(number):
number += 1000
print(number)
return add(s)
number(1, 2, 3, 4, 5, 6) # 结果:--->1021
# 内部函数与外部函数的差别
def a():
x = 100
y = 1000
s = x + y
def b():
nonlocal s
m = 0
m += s
print(m)
s += 10000
print(s)
return b()
a()
# 结果:--->1100
# 结果:--->11100
在嵌套函数中也有内外部函数之分,这关系到内外部函数中值的调用问题,一般的外部函数的变量是可以给内部函数使用的,但是一般情况下也不允许外部函数对其进行修改。内部函数查找变量的过程:内部 —> 外部 —> 全局 —> 系统变量(builtins)
闭包的条件
注意:可以只打开和关闭文件,不进行任何读写操作。
在python,使用open函数,可以打开一个已经存在的文件,或者创建一个新文件,语法如下:
open(name, mode)
name:是要打开的目标文件名的字符串(可以包含文件所在的具体路径)。
mode:设置打开文件的模式(访问模式):只读、写入、追加等。
打开文件模式
模式 | 描述 |
---|---|
r | 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 |
rb | 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。 |
r+ | 打开一个文件用于读写。文件指针将会放在文件的开头。 |
rb+ | 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。 |
w | 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb | 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
w+ | 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb+ | 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
a | 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
ab | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
a+ | 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。 |
ab+ | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。 |
f = open('test.txt', 'w')
注意:此时的
f
是open
函数的文件对象。
对象对象.write('内容')
# 1. 打开文件
f = open('test.txt', 'w')
# 2.文件写入
f.write('hello world')
# 3. 关闭文件
f.close()
注意:
w
和a
模式:如果文件不存在则创建该文件;如果文件存在,w
模式先清空再写入,a
模式直接末尾追加。r
模式:如果文件不存在则报错。
文件对象.read(num)
num表示要从文件中读取的数据的长度(单位是字节),如果没有传入num,那么就表示读取文件中所有的数据。
readlines可以按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素。
f = open('test.txt')
content = f.readlines()
# ['hello world\n', 'abcdefg\n', 'aaa\n', 'bbb\n', 'ccc']
print(content)
# 关闭文件
f.close()
readline()一次读取一行内容。
f = open('test.txt')
content = f.readline()
print(f'第一行:{content}')
content = f.readline()
print(f'第二行:{content}')
# 关闭文件
f.close()
作用:用来移动文件指针。
语法如下:
文件对象.seek(偏移量, 起始位置)
起始位置:
- 0:文件开头
- 1:当前位置
- 2:文件结尾
文件对象.close()
需求:用户输入当前目录下任意文件名,程序完成对该文件的备份功能(备份文件名为xx[备份]后缀,例如:test[备份].txt)。
old_name = input('请输入您要备份的文件名:')
规划备份文件名
2.1 提取目标文件后缀
2.2 组织备份的文件名,xx[备份]后缀
# 2.1 提取文件后缀点的下标
index = old_name.rfind('.')
# print(index) # 后缀中.的下标
# print(old_name[:index]) # 源文件名(无后缀)
# 2.2 组织新文件名 旧文件名 + [备份] + 后缀
new_name = old_name[:index] + '[备份]' + old_name[index:]
# 打印新文件名(带后缀)
# print(new_name)
备份文件写入数据
打开源文件 和 备份文件
将源文件数据写入备份文件
关闭文件
# 3.1 打开文件
old_f = open(old_name, 'rb')
new_f = open(new_name, 'wb')
# 3.2 将源文件数据写入备份文件
while True:
con = old_f.read(1024)
if len(con) == 0:
break
new_f.write(con)
# 3.3 关闭文件
old_f.close()
new_f.close()
在Python中文件和文件夹的操作要借助os模块里面的相关功能,具体步骤如下:
import os
os
模块相关功能os.函数名()
os.rename(目标文件名, 新文件名)
os.remove(目标文件名)
os.mkdir(文件夹名字)
os.rmdir(文件夹名字)
os.getcwd()
os.chdir(目录)
os.listdir(目录)
文件操作步骤
文件对象 = open(目标文件, 访问模式)
操作
文件对象.read()
文件对象.readlines()
文件对象.readline()
文件对象.write()
关闭
文件对象.close()
主访问模式
文件和文件夹操作
面向对象是一种抽象化的编程思想,很多编程语言中都有的一种思想。
例如:洗衣服
思考:几种途径可以完成洗衣服?
答: 手洗 和 机洗。
手洗:找盆 - 放水 - 加洗衣粉 - 浸泡 - 搓洗 - 拧干水 - 倒水 - 漂洗N次 - 拧干 - 晾晒。
机洗:打开洗衣机 - 放衣服 - 加洗衣粉 - 按下开始按钮 - 晾晒。
思考:对比两种洗衣服途径,同学们发现了什么?
答:机洗更简单
思考:机洗,只需要找到一台洗衣机,加入简单操作就可以完成洗衣服的工作,而不需要关心洗衣机内部发生了什么事情。
总结:面向对象就是将编程当成是一个事物,对外界来说,事物是直接使用的,不用去管他内部的情况。而编程就是设置事物能够做什么事。
思考:洗衣机洗衣服描述过程中,洗衣机其实就是一个事物,即对象,洗衣机对象哪来的呢?
答:洗衣机是由工厂工人制作出来。
思考:工厂工人怎么制作出的洗衣机?
答:工人根据设计师设计的功能图纸制作洗衣机。
总结:图纸 → 洗衣机 → 洗衣服。
在面向对象编程过程中,有两个重要组成部分:类 和 对象。
类和对象的关系:用类去创建一个对象。
类是对一系列具有相同特征和行为的事物的统称,是一个抽象的概念,不是真实存在的事物。
类比如是制造洗衣机时要用到的图纸,也就是说类是用来创建对象。
对象是类创建出来的真实存在的事物,例如:洗衣机。
注意:开发中,先有类,再有对象。
Python2中类分为:经典类 和 新式类
class 类名():
代码
......
注意:类名要满足标识符命名规则,同时遵循大驼峰命名习惯。
class Washer():
def wash(self):
print('我会洗衣服')
不由任意内置类型派生出的类,称之为经典类
class 类名:
代码
......
对象又名实例。
对象名 = 类名()
# 创建对象
haier1 = Washer()
# <__main__.Washer object at 0x0000018B7B224240>
print(haier1)
# haier对象调用实例方法
haier1.wash()
注意:创建对象的过程也叫实例化对象。
self指的是调用该函数的对象。
# 1. 定义类
class Washer():
def wash(self):
print('我会洗衣服')
# <__main__.Washer object at 0x0000024BA2B34240>
print(self)
# 2. 创建对象
haier1 = Washer()
# <__main__.Washer object at 0x0000018B7B224240>
print(haier1)
# haier1对象调用实例方法
haier1.wash()
haier2 = Washer()
# <__main__.Washer object at 0x0000022005857EF0>
print(haier2)
注意:打印对象和self得到的结果是一致的,都是当前对象的内存中存储地址。
添加和获取对象属性
属性即是特征,比如:洗衣机的宽度、高度、重量…
对象属性既可以在类外面添加和获取,也能在类里面添加和获取。
对象名.属性名 = 值
haier1.width = 500
haier1.height = 800
对象名.属性名
print(f'haier1洗衣机的宽度是{haier1.width}')
print(f'haier1洗衣机的高度是{haier1.height}')
self.属性名
# 定义类
class Washer():
def print_info(self):
# 类里面获取实例属性
print(f'haier1洗衣机的宽度是{self.width}')
print(f'haier1洗衣机的高度是{self.height}')
# 创建对象
haier1 = Washer()
# 添加实例属性
haier1.width = 500
haier1.height = 800
haier1.print_info()
在Python中,__xx__()
的函数叫做魔法方法,指的是具有特殊功能的函数。
__init__()
思考:洗衣机的宽度高度是与生俱来的属性,可不可以在生产过程中就赋予这些属性呢?
答:理应如此。
__init__()
方法的作用:初始化对象。
class Washer():
# 定义初始化功能的函数
def __init__(self):
# 添加实例属性
self.width = 500
self.height = 800
def print_info(self):
# 类里面调用实例属性
print(f'洗衣机的宽度是{self.width}, 高度是{self.height}')
haier1 = Washer()
haier1.print_info()
注意:
__init__()
方法,在创建一个对象时默认被调用,不需要手动调用__init__(self)
中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递过去。
__init__()
思考:一个类可以创建多个对象,如何对不同的对象设置不同的初始化属性呢?
答:传参数。
class Washer():
def __init__(self, width, height):
self.width = width
self.height = height
def print_info(self):
print(f'洗衣机的宽度是{self.width}')
print(f'洗衣机的高度是{self.height}')
haier1 = Washer(10, 20)
haier1.print_info()
haier2 = Washer(30, 40)
haier2.print_info()
__str__()
当使用print输出对象的时候,默认打印对象的内存地址。如果类定义了__str__
方法,那么就会打印从在这个方法中 return 的数据。
class Washer():
def __init__(self, width, height):
self.width = width
self.height = height
def __str__(self):
return '这是海尔洗衣机的说明书'
haier1 = Washer(10, 20)
# 这是海尔洗衣机的说明书
print(haier1)
__del__()
当删除对象时,python解释器也会默认调用__del__()
方法。
class Washer():
def __init__(self, width, height):
self.width = width
self.height = height
def __del__(self):
print(f'{self}对象已经被删除')
haier1 = Washer(10, 20)
# <__main__.Washer object at 0x0000026118223278>对象已经被删除
del haier1
面向对象重要组成部分
class 类名():
代码
对象名 = 类名()
添加对象属性
对象名.属性名 = 值
self.属性名 = 值
获取对象属性
对象名.属性名
self.属性名
魔法方法
__init__()
: 初始化__str__()
:输出对象信息__del__()
:删除对象时调用生活中的继承,一般指的是子女继承父辈的财产。
不由任意内置类型派生出的类,称之为经典类。
class 类名:
代码
......
class 类名(object):
代码
Python面向对象的继承指的是多个类之间的所属关系,即子类默认继承父类的所有属性和方法,具体如下:
# 父类A
class A(object):
def __init__(self):
self.num = 1
def info_print(self):
print(self.num)
# 子类B
class B(A):
pass
result = B()
result.info_print() # 1
在Python中,所有类默认继承object类,object类是顶级类或基类;其他子类叫做派生类。
故事主线:一个煎饼果子老师傅,在煎饼果子界摸爬滚打多年,研发了一套精湛的摊煎饼果子的技术。师父要把这套技术传授给他的唯一的最得意的徒弟。
分析:徒弟是不是要继承师父的所有技术?
# 1. 师父类
class Master(object):
def __init__(self):
self.kongfu = '[古法煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
# 2. 徒弟类
class Prentice(Master):
pass
# 3. 创建对象daqiu
daqiu = Prentice()
# 4. 对象访问实例属性
print(daqiu.kongfu)
# 5. 对象调用实例方法
daqiu.make_cake()
故事推进:daqiu是个爱学习的好孩子,想学习更多的煎饼果子技术,于是,在百度搜索到黑马程序员,报班学习煎饼果子技术。
所谓多继承意思就是一个类同时继承了多个父类。
class Master(object):
def __init__(self):
self.kongfu = '[古法煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
# 创建学校类
class School(object):
def __init__(self):
self.kongfu = '[黑马煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
class Prentice(School, Master):
pass
daqiu = Prentice()
print(daqiu.kongfu)
daqiu.make_cake()
注意:当一个类有多个父类的时候,默认使用第一个父类的同名属性和方法。
故事:daqiu掌握了师父和培训的技术后,自己潜心钻研出自己的独门配方的一套全新的煎饼果子技术。
class Master(object):
def __init__(self):
self.kongfu = '[古法煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
class School(object):
def __init__(self):
self.kongfu = '[黑马煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
# 独创配方
class Prentice(School, Master):
def __init__(self):
self.kongfu = '[独创煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
daqiu = Prentice()
print(daqiu.kongfu)
daqiu.make_cake()
print(Prentice.__mro__)
子类和父类具有同名属性和方法,默认使用子类的同名属性和方法。
故事:很多顾客都希望也能吃到古法和黑马的技术的煎饼果子。
class Master(object):
def __init__(self):
self.kongfu = '[古法煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
class School(object):
def __init__(self):
self.kongfu = '[黑马煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
class Prentice(School, Master):
def __init__(self):
self.kongfu = '[独创煎饼果子配方]'
def make_cake(self):
# 如果是先调用了父类的属性和方法,父类属性会覆盖子类属性,故在调用属性前,先调用自己子类的初始化
self.__init__()
print(f'运用{self.kongfu}制作煎饼果子')
# 调用父类方法,但是为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化
def make_master_cake(self):
Master.__init__(self)
Master.make_cake(self)
def make_school_cake(self):
School.__init__(self)
School.make_cake(self)
daqiu = Prentice()
daqiu.make_cake()
daqiu.make_master_cake()
daqiu.make_school_cake()
daqiu.make_cake()
故事:N年后,daqiu老了,想要把所有技术传承给自己的徒弟。
class Master(object):
def __init__(self):
self.kongfu = '[古法煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
class School(object):
def __init__(self):
self.kongfu = '[黑马煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
class Prentice(School, Master):
def __init__(self):
self.kongfu = '[独创煎饼果子配方]'
def make_cake(self):
self.__init__()
print(f'运用{self.kongfu}制作煎饼果子')
def make_master_cake(self):
Master.__init__(self)
Master.make_cake(self)
def make_school_cake(self):
School.__init__(self)
School.make_cake(self)
# 徒孙类
class Tusun(Prentice):
pass
xiaoqiu = Tusun()
xiaoqiu.make_cake()
xiaoqiu.make_school_cake()
xiaoqiu.make_master_cake()
class Master(object):
def __init__(self):
self.kongfu = '[古法煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
class School(Master):
def __init__(self):
self.kongfu = '[黑马煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
# 方法2.1
# super(School, self).__init__()
# super(School, self).make_cake()
# 方法2.2
super().__init__()
super().make_cake()
class Prentice(School):
def __init__(self):
self.kongfu = '[独创煎饼果子技术]'
def make_cake(self):
self.__init__()
print(f'运用{self.kongfu}制作煎饼果子')
# 子类调用父类的同名方法和属性:把父类的同名属性和方法再次封装
def make_master_cake(self):
Master.__init__(self)
Master.make_cake(self)
def make_school_cake(self):
School.__init__(self)
School.make_cake(self)
# 一次性调用父类的同名属性和方法
def make_old_cake(self):
# 方法一:代码冗余;父类类名如果变化,这里代码需要频繁修改
# Master.__init__(self)
# Master.make_cake(self)
# School.__init__(self)
# School.make_cake(self)
# 方法二: super()
# 方法2.1 super(当前类名, self).函数()
# super(Prentice, self).__init__()
# super(Prentice, self).make_cake()
# 方法2.2 super().函数()
super().__init__()
super().make_cake()
daqiu = Prentice()
daqiu.make_old_cake()
注意:使用super() 可以自动查找父类。调用顺序遵循
__mro__
类属性的顺序。比较适合单继承使用。
在Python中,可以为实例属性和方法设置私有权限,即设置某个实例属性或实例方法不继承给子类。
故事:daqiu把技术传承给徒弟的同时,不想把自己的钱(2000000个亿)继承给徒弟,这个时候就要为
钱
这个实例属性设置私有权限。
设置私有权限的方法:在属性名和方法名 前面 加上两个下划线 __。
class Master(object):
def __init__(self):
self.kongfu = '[古法煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
class School(object):
def __init__(self):
self.kongfu = '[黑马煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
class Prentice(School, Master):
def __init__(self):
self.kongfu = '[独创煎饼果子配方]'
# 定义私有属性
self.__money = 2000000
# 定义私有方法
def __info_print(self):
print(self.kongfu)
print(self.__money)
def make_cake(self):
self.__init__()
print(f'运用{self.kongfu}制作煎饼果子')
def make_master_cake(self):
Master.__init__(self)
Master.make_cake(self)
def make_school_cake(self):
School.__init__(self)
School.make_cake(self)
# 徒孙类
class Tusun(Prentice):
pass
daqiu = Prentice()
# 对象不能访问私有属性和私有方法
# print(daqiu.__money)
# daqiu.__info_print()
xiaoqiu = Tusun()
# 子类无法继承父类的私有属性和私有方法
# print(xiaoqiu.__money) # 无法访问实例属性__money
# xiaoqiu.__info_print()
注意:私有属性和私有方法只能在类里面访问和修改。
在Python中,一般定义函数名get_xx
用来获取私有属性,定义set_xx
用来修改私有属性值。
class Master(object):
def __init__(self):
self.kongfu = '[古法煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
class School(object):
def __init__(self):
self.kongfu = '[黑马煎饼果子配方]'
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
class Prentice(School, Master):
def __init__(self):
self.kongfu = '[独创煎饼果子配方]'
self.__money = 2000000
# 获取私有属性
def get_money(self):
return self.__money
# 修改私有属性
def set_money(self):
self.__money = 500
def __info_print(self):
print(self.kongfu)
print(self.__money)
def make_cake(self):
self.__init__()
print(f'运用{self.kongfu}制作煎饼果子')
def make_master_cake(self):
Master.__init__(self)
Master.make_cake(self)
def make_school_cake(self):
School.__init__(self)
School.make_cake(self)
# 徒孙类
class Tusun(Prentice):
pass
daqiu = Prentice()
xiaoqiu = Tusun()
# 调用get_money函数获取私有属性money的值
print(xiaoqiu.get_money())
# 调用set_money函数修改私有属性money的值
xiaoqiu.set_money()
print(xiaoqiu.get_money())
继承的特点
super()方法快速调用父类方法
私有权限
class 类名():
# 私有属性
__属性名 = 值
# 私有方法
def __函数名(self):
代码
多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)。
class Dog(object):
def work(self): # 父类提供统一的方法,哪怕是空方法
print('指哪打哪...')
class ArmyDog(Dog): # 继承Dog类
def work(self): # 子类重写父类同名方法
print('追击敌人...')
class DrugDog(Dog):
def work(self):
print('追查毒品...')
class Person(object):
def work_with_dog(self, dog): # 传入不同的对象,执行不同的代码,即不同的work函数
dog.work()
ad = ArmyDog()
dd = DrugDog()
daqiu = Person()
daqiu.work_with_dog(ad)
daqiu.work_with_dog(dd)
class Dog(object):
tooth = 10
wangcai = Dog()
xiaohei = Dog()
print(Dog.tooth) # 10
print(wangcai.tooth) # 10
print(xiaohei.tooth) # 10
类属性的优点
- 记录的某项数据 始终保持一致时,则定义类属性。
- 实例属性 要求 每个对象 为其 单独开辟一份内存空间 来记录数据,而 类属性 为全类所共有 ,仅占用一份内存,更加节省内存空间。
类属性只能通过类对象修改,不能通过实例对象修改,如果通过实例对象修改类属性,表示的是创建了一个实例属性。
class Dog(object):
tooth = 10
wangcai = Dog()
xiaohei = Dog()
# 修改类属性
Dog.tooth = 12
print(Dog.tooth) # 12
print(wangcai.tooth) # 12
print(xiaohei.tooth) # 12
# 不能通过对象修改属性,如果这样操作,实则是创建了一个实例属性
wangcai.tooth = 20
print(Dog.tooth) # 12
print(wangcai.tooth) # 20
print(xiaohei.tooth) # 12
class Dog(object):
def __init__(self):
self.age = 5
def info_print(self):
print(self.age)
wangcai = Dog()
print(wangcai.age) # 5
# print(Dog.age) # 报错:实例属性不能通过类访问
wangcai.info_print() # 5
@classmethod
来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls
作为第一个参数。class Dog(object):
__tooth = 10
@classmethod
def get_tooth(cls):
return cls.__tooth
wangcai = Dog()
result = wangcai.get_tooth()
print(result) # 10
@staticmethod
来进行修饰,静态方法既不需要传递类对象也不需要传递实例对象(形参没有self/cls)。class Dog(object):
@staticmethod
def info_print():
print('这是一个狗类,用于创建狗实例....')
wangcai = Dog()
# 静态方法既可以使用对象访问又可以使用类访问
wangcai.info_print()
Dog.info_print()
@classmethod
def xx():
代码
@staticmethod
def xx():
代码
当检测到一个错误时,解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的"异常"。
例如:以r
方式打开一个不存在的文件。
open('test.txt', 'r')
try:
可能发生错误的代码
except:
如果出现异常执行的代码
需求:尝试以r
模式打开文件,如果文件不存在,则以w
方式打开。
try:
f = open('test.txt', 'r')
except:
f = open('test.txt', 'w')
try:
可能发生错误的代码
except 异常类型:
如果捕获到该异常类型执行的代码
try:
print(num)
except NameError:
print('有错误')
注意:
- 如果尝试执行的代码的异常类型和要捕获的异常类型不一致,则无法捕获异常。
- 一般try下方只放一行尝试执行的代码。
当捕获多个异常时,可以把要捕获的异常类型的名字,放到except 后,并使用元组的方式进行书写。
try:
print(1/0)
except (NameError, ZeroDivisionError):
print('有错误')
try:
print(num)
except (NameError, ZeroDivisionError) as result:
print(result)
Exception是所有程序异常类的父类。
try:
print(num)
except Exception as result:
print(result)
else表示的是如果没有异常要执行的代码。
try:
print(1)
except Exception as result:
print(result)
else:
print('我是else,是没有异常的时候执行的代码')
finally表示的是无论是否异常都要执行的代码,例如关闭文件。
try:
f = open('test.txt', 'r')
except Exception as result:
f = open('test.txt', 'w')
else:
print('没有异常,真开心')
finally:
f.close()
体验异常传递
需求:
1. 尝试只读方式打开test.txt文件,如果文件存在则读取文件内容,文件不存在则提示用户即可。
2. 读取内容要求:尝试循环读取内容,读取过程中如果检测到用户意外终止程序,则except
捕获异常并提示用户。
import time
try:
f = open('test.txt')
try:
while True:
content = f.readline()
if len(content) == 0:
break
time.sleep(2)
print(content)
except:
# 如果在读取文件的过程中,产生了异常,那么就会捕获到
# 比如 按下了 ctrl+c
print('意外终止了读取数据')
finally:
f.close()
print('关闭文件')
except:
print("没有这个文件")
在Python中,抛出自定义异常的语法为 raise 异常类对象
。
需求:密码长度不足,则报异常(用户输入密码,如果输入的长度不足3位,则报错,即抛出自定义异常,并捕获该异常)。
# 自定义异常类,继承Exception
class ShortInputError(Exception):
def __init__(self, length, min_len):
self.length = length
self.min_len = min_len
# 设置抛出异常的描述信息
def __str__(self):
return f'你输入的长度是{self.length}, 不能少于{self.min_len}个字符'
def main():
try:
con = input('请输入密码:')
if len(con) < 3:
raise ShortInputError(len(con), 3)
except Exception as result:
print(result)
else:
print('密码已经输入完成')
main()
try:
可能发生异常的代码
except:
如果出现异常执行的代码
else:
没有异常执行的代码
finally:
无论是否异常都要执行的代码
except 异常类型:
代码
except 异常类型 as xx:
代码
# 1. 自定义异常类
class 异常类类名(Exception):
代码
# 设置抛出异常的描述信息
def __str__(self):
return ...
# 2. 抛出异常
raise 异常类名()
# 捕获异常
except Exception...
Python 模块(Module),是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句。
模块能定义函数,类和变量,模块里也能包含可执行的代码。
# 1. 导入模块
import 模块名
import 模块名1, 模块名2...
# 2. 调用功能
模块名.功能名()
import math
print(math.sqrt(9)) # 3.0
from 模块名 import 功能1, 功能2, 功能3...
from math import sqrt
print(sqrt(9))
from 模块名 import *
from math import *
print(sqrt(9))
# 模块定义别名
import 模块名 as 别名
# 功能定义别名
from 模块名 import 功能 as 别名
# 模块别名
import time as tt
tt.sleep(2)
print('hello')
# 功能别名
from time import sleep as sl
sl(2)
print('hello')
在Python中,每个Python文件都可以作为一个模块,模块的名字就是文件的名字。也就是说自定义模块名必须要符合标识符命名规则。
新建一个Python文件,命名为my_module1.py
,并定义testA
函数。
def testA(a, b):
print(a + b)
在实际开中,当一个开发人员编写完一个模块后,为了让模块能够在项目中达到想要的效果,这个开发人员会自行在py文件中添加一些测试信息.,例如,在my_module1.py
文件中添加测试代码。
def testA(a, b):
print(a + b)
testA(1, 1)
此时,无论是当前文件,还是其他已经导入了该模块的文件,在运行的时候都会自动执行testA
函数的调用。
解决办法如下:
def testA(a, b):
print(a + b)
# 只在当前文件中调用该函数,其他导入的文件内不符合该条件,则不执行testA函数调用
if __name__ == '__main__':
testA(1, 1)
import my_module1
my_module1.testA(1, 1)
如果使用from .. import ..
或from .. import *
导入多个模块的时候,且模块内有同名功能。当调用这个同名功能的时候,调用到的是后面导入的模块的功能。
# 模块1代码
def my_test(a, b):
print(a + b)
# 模块2代码
def my_test(a, b):
print(a - b)
# 导入模块和调用功能代码
from my_module1 import my_test
from my_module2 import my_test
# my_test函数是模块2中的函数
my_test(1, 1)
当导入一个模块,Python解析器对模块位置的搜索顺序是:
模块搜索路径存储在system模块的sys.path变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。
使用from 模块名 import 功能
的时候,如果功能名字重复,调用到的是最后定义或导入的功能。__all__
如果一个模块文件中有__all__
变量,当使用from xxx import *
导入时,只能导入这个列表中的元素。
__all__ = ['testA']
def testA():
print('testA')
def testB():
print('testB')
from my_module1 import *
testA()
testB()
包将有联系的模块组织在一起,即放到同一个文件夹下,并且在这个文件夹创建一个名字为__init__.py
文件,那么这个文件夹就称之为包。
[New] — [Python Package] — 输入包名 — [OK] — 新建功能模块(有联系的模块)。
注意:新建包后,包内部会自动创建__init__.py
文件,这个文件控制着包的导入行为。
mypackage
my_module1
和 my_module2
# my_module1
print(1)
def info_print1():
print('my_module1')
# my_module2
print(2)
def info_print2():
print('my_module2')
import 包名.模块名
包名.模块名.目标
import my_package.my_module1
my_package.my_module1.info_print1()
注意:必须在__init__.py
文件中添加__all__ = []
,控制允许导入的模块列表。
from 包名 import *
模块名.目标
from my_package import *
my_module1.info_print1()
import 模块名
from 模块名 import 目标
from 模块名 import *
import 包名.模块名
from 包名 import *
__all__ = []
:允许导入的模块或功能列表迭代是Python最强大的功能之一,是访问集合元素的一种方式。
迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器有两个基本的方法:iter() 和 next()。
字符串,列表或元组对象都可用于创建迭代器:
\>>> list=[1,2,3,4]
\>>> it = iter(list) # 创建迭代器对象
\>>> **print** (next(it)) # 输出迭代器的下一个元素
1
\>>> **print** (next(it))
2
\>>>
迭代器对象可以使用常规for语句进行遍历
#!/usr/bin/python3
list=[1,2,3,4]
it = iter(list) # 创建迭代器对象
for x in it:
print (x, end=" ")
结果:1 2 3 4
也可以使用 next() 函数:
#!/usr/bin/python3
import sys # 引入 sys 模块
list=[1,2,3,4]
it = iter(list) # 创建迭代器对象
while True:
try:
print (next(it))
except StopIteration:
sys.exit()
结果:1
2
3
4
把一个类作为一个迭代器使用需要在类中实现两个方法 iter() 与 next() 。
如果你已经了解的面向对象编程,就知道类都有一个构造函数,Python 的构造函数为 init(), 它会在对象初始化的时候执行。
更多内容查阅:Python3 面向对象
iter() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 next() 方法并通过 StopIteration 异常标识迭代的完成。
next() 方法(Python 2 里是 next())会返回下一个迭代器对象。
创建一个返回数字的迭代器,初始值为 1,逐步递增 1:
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
x = self.a
self.a += 1
return x
myclass = MyNumbers() myiter = iter(myclass)
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
结果:
1
2
3
4
5
StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 next() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
在 20 次迭代后停止执行:
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a <= 20:
x = self.a
self.a += 1
return x
else:
raise StopIteration myclass = MyNumbers() myiter = iter(myclass)
for x in myiter: print(x)
结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
在 Python 中,使用了 yield 的函数被称为生成器(generator)。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
调用一个生成器函数,返回的是一个迭代器对象。
以下实例使用 yield 实现斐波那契数列:
\#!/usr/bin/python3
import sys def fibonacci(n): # 生成器函数 - 斐波那契
a, b, counter = 0, 1, 0
while True:
if (counter > n):
return
yield a
a, b = b, a + b
counter += 1 f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
while True:
try:
print (next(f), end=" ")
except StopIteration:
sys.exit()
#输出结果:0 1 1 2 3 5 8 13 21 34 55
在前面的几个章节中我们基本上是用 python 解释器来编程,如果你从 Python 解释器退出再进入,那么你定义的所有的方法和变量就都消失了。
为此 Python 提供了一个办法,把这些定义存放在文件中,为一些脚本或者交互式的解释器实例使用,这个文件被称为模块。
模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。这也是使用 python 标准库的方法。
下面是一个使用 python 标准库中模块的例子。
#!/usr/bin/python3 # 文件名: using_sys.py import sys print('命令行参数如下:') for i in sys.argv: print(i) print('\n\nPython 路径为:', sys.path, '\n')
结果:$ python using_sys.py 参数1 参数2
命令行参数如下:
using_sys.py
参数1
参数2
Python 路径为: ['/root', '/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu', '/usr/lib/python3.4/lib-dynload', '/usr/local/lib/python3.4/dist-packages', '/usr/lib/python3/dist-packages']
想使用 Python 源文件,只需在另一个源文件里执行 import 语句,语法如下:
import module1[, module2[,... moduleN]
当解释器遇到 import 语句,如果模块在当前的搜索路径就会被导入。
搜索路径是一个解释器会先进行搜索的所有目录的列表。如想要导入模块 support,需要把命令放在脚本的顶端:
\#!/usr/bin/python3 # Filename: support.py
def print_func( par ):
print ("Hello : ", par)
return
test.py 引入 support 模块:
#!/usr/bin/python3
# Filename: test.py
# 导入模块
import support
# 现在可以调用模块里包含的函数了
support.print_func("Runoob")
以上实例输出结果:
$ python3 test.py
Hello : Runoob
一个模块只会被导入一次,不管你执行了多少次import。这样可以防止导入模块被一遍又一遍地执行。
当我们使用import语句的时候,Python解释器是怎样找到对应的文件的呢?
这就涉及到Python的搜索路径,搜索路径是由一系列目录名组成的,Python解释器就依次从这些目录中去寻找所引入的模块。
这看起来很像环境变量,事实上,也可以通过定义环境变量的方式来确定搜索路径。
搜索路径是在Python编译或安装的时候确定的,安装新的库应该也会修改。搜索路径被存储在sys模块中的path变量,做一个简单的实验,在交互式解释器中,输入以下代码:
>>> import sys
>>> sys.path
['', '/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu', '/usr/lib/python3.4/lib-dynload', '/usr/local/lib/python3.4/dist-packages', '/usr/lib/python3/dist-packages']
>>>
sys.path 输出是一个列表,其中第一项是空串’',代表当前目录(若是从一个脚本中打印出来的话,可以更清楚地看出是哪个目录),亦即我们执行python解释器的目录(对于脚本的话就是运行的脚本所在的目录)。
因此若像我一样在当前目录下存在与要引入模块同名的文件,就会把要引入的模块屏蔽掉。
了解了搜索路径的概念,就可以在脚本中修改sys.path来引入一些不在搜索路径中的模块。
现在,在解释器的当前目录或者 sys.path 中的一个目录里面来创建一个fibo.py的文件,代码如下:
\# 斐波那契(fibonacci)数列模块
def fib(n): # 定义到 n 的斐波那契数列
a, b = 0, 1
while b < n:
print(b, end=' ')
a, b = b, a+b
print()
def fib2(n): # 返回到 n 的斐波那契数列
result = []
a, b = 0, 1
while b < n:
result.append(b)
a, b = b, a+b
return result
然后进入Python解释器,使用下面的命令导入这个模块:
>>> import fibo
这样做并没有把直接定义在fibo中的函数名称写入到当前符号表里,只是把模块fibo的名字写到了那里。
可以使用模块名称来访问函数:
>>>fibo.fib(1000) 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 >>> fibo.fib2(100) [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] >>> fibo.__name__ 'fibo'
如果你打算经常使用一个函数,你可以把它赋给一个本地的名称:
>>> fib = fibo.fib
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377
Python 的 from 语句让你从模块中导入一个指定的部分到当前命名空间中,语法如下:
from modname import name1[, name2[, ... nameN]]
例如,要导入模块 fibo 的 fib 函数,使用如下语句:
>>> from fibo import fib, fib2
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377
这个声明不会把整个fibo模块导入到当前的命名空间中,它只会将fibo里的fib函数引入进来。
把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:
from modname import *
这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。
模块除了方法定义,还可以包括可执行的代码。这些代码一般用来初始化这个模块。这些代码只有在第一次被导入时才会被执行。
每个模块有各自独立的符号表,在模块内部为所有的函数当作全局符号表来使用。
所以,模块的作者可以放心大胆的在模块内部使用这些全局变量,而不用担心把其他用户的全局变量搞混。
从另一个方面,当你确实知道你在做什么的话,你也可以通过 modname.itemname 这样的表示法来访问模块内的函数。
模块是可以导入其他模块的。在一个模块(或者脚本,或者其他地方)的最前面使用 import 来导入一个模块,当然这只是一个惯例,而不是强制的。被导入的模块的名称将被放入当前操作的模块的符号表中。
还有一种导入的方法,可以使用 import 直接把模块内(函数,变量的)名称导入到当前操作模块。比如:
>>> from fibo import fib, fib2
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377
这种导入的方法不会把被导入的模块的名称放在当前的字符表中(所以在这个例子里面,fibo 这个名称是没有定义的)。
这还有一种方法,可以一次性的把模块中的所有(函数,变量)名称都导入到当前模块的字符表:
>>> from fibo import *
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377
这将把所有的名字都导入进来,但是那些由单一下划线(_)开头的名字不在此例。大多数情况, Python程序员不使用这种方法,因为引入的其它来源的命名,很可能覆盖了已有的定义。
一个模块被另一个程序第一次引入时,其主程序将运行。如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用__name__属性来使该程序块仅在该模块自身运行时执行。
#!/usr/bin/python3
# Filename: using_name.py
if __name__ == '__main__':
print('程序自身在运行')
else:
print('我来自另一模块')
运行输出如下:
$ python using_name.py
程序自身在运行
$ python
>>> import using_name
我来自另一模块
>>>
说明: 每个模块都有一个__name__属性,当其值是’main’时,表明该模块自身在运行,否则是被引入。
说明:name 与 main 底下是双下划线, _ _ 是这样去掉中间的那个空格。
内置的函数 dir() 可以找到模块内定义的所有名称。以一个字符串列表的形式返回:
>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__loader__', '__name__',
'__package__', '__stderr__', '__stdin__', '__stdout__',
'_clear_type_cache', '_current_frames', '_debugmallocstats', '_getframe',
'_home', '_mercurial', '_xoptions', 'abiflags', 'api_version', 'argv',
'base_exec_prefix', 'base_prefix', 'builtin_module_names', 'byteorder',
'call_tracing', 'callstats', 'copyright', 'displayhook',
'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix',
'executable', 'exit', 'flags', 'float_info', 'float_repr_style',
'getcheckinterval', 'getdefaultencoding', 'getdlopenflags',
'getfilesystemencoding', 'getobjects', 'getprofile', 'getrecursionlimit',
'getrefcount', 'getsizeof', 'getswitchinterval', 'gettotalrefcount',
'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
'intern', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path',
'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1',
'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit',
'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout',
'thread_info', 'version', 'version_info', 'warnoptions']
如果没有给定参数,那么 dir() 函数会罗列出当前定义的所有名称:
>>> a = [1, 2, 3, 4, 5]
>>> import fibo
>>> fib = fibo.fib
>>> dir() # 得到一个当前模块中定义的属性列表
['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
>>> a = 5 # 建立一个新的变量 'a'
>>> dir()
['__builtins__', '__doc__', '__name__', 'a', 'sys']
>>>
>>> del a # 删除变量名a
>>>
>>> dir()
['__builtins__', '__doc__', '__name__', 'sys']
>>>
Python 本身带着一些标准的模块库,在 Python 库参考文档中将会介绍到(就是后面的"库参考文档")。
有些模块直接被构建在解析器里,这些虽然不是一些语言内置的功能,但是他却能很高效的使用,甚至是系统级调用也没问题。
这些组件会根据不同的操作系统进行不同形式的配置,比如 winreg 这个模块就只会提供给 Windows 系统。
应该注意到这有一个特别的模块 sys ,它内置在每一个 Python 解析器中。变量 sys.ps1 和 sys.ps2 定义了主提示符和副提示符所对应的字符串:
>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print('Runoob!')
Runoob!
C>
包是一种管理 Python 模块命名空间的形式,采用"点模块名称"。
比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B 。
就好像使用模块的时候,你不用担心不同模块之间的全局变量相互影响一样,采用点模块名称这种形式也不用担心不同库之间的模块重名的情况。
这样不同的作者都可以提供 NumPy 模块,或者是 Python 图形库。
不妨假设你想设计一套统一处理声音文件和数据的模块(或者称之为一个"包")。
现存很多种不同的音频文件格式(基本上都是通过后缀名区分的,例如: .wav,:file:.aiff,:file:.au,),所以你需要有一组不断增加的模块,用来在不同的格式之间转换。
并且针对这些音频数据,还有很多不同的操作(比如混音,添加回声,增加均衡器功能,创建人造立体声效果),所以你还需要一组怎么也写不完的模块来处理这些操作。
这里给出了一种可能的包结构(在分层的文件系统中):
sound/ 顶层包
__init__.py 初始化 sound 包
formats/ 文件格式转换子包
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ 声音效果子包
__init__.py
echo.py
surround.py
reverse.py
...
filters/ filters 子包
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
在导入一个包的时候,Python 会根据 sys.path 中的目录来寻找这个包中包含的子目录。
目录只有包含一个叫做 init.py 的文件才会被认作是一个包,主要是为了避免一些滥俗的名字(比如叫做 string)不小心的影响搜索路径中的有效模块。
最简单的情况,放一个空的 :file:init.py就可以了。当然这个文件中也可以包含一些初始化代码或者为(将在后面介绍的) __all__变量赋值。
用户可以每次只导入一个包里面的特定模块,比如:
import sound.effects.echo
这将会导入子模块:sound.effects.echo。 他必须使用全名去访问:
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
还有一种导入子模块的方法是:
from sound.effects import echo
这同样会导入子模块: echo,并且他不需要那些冗长的前缀,所以他可以这样使用:
echo.echofilter(input, output, delay=0.7, atten=4)
还有一种变化就是直接导入一个函数或者变量:
from sound.effects.echo import echofilter
同样的,这种方法会导入子模块: echo,并且可以直接使用他的 echofilter() 函数:
echofilter(input, output, delay=0.7, atten=4)
注意当使用 from package import item 这种形式的时候,对应的 item 既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量。
import 语法会首先把 item 当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,抛出一个 :exc:ImportError 异常。
反之,如果使用形如 import item.subitem.subsubitem 这种导入形式,除了最后一项,都必须是包,而最后一项则可以是模块或者是包,但是不可以是类,函数或者变量的名字。
如果我们使用 from sound.effects import * 会发生什么呢?
Python 会进入文件系统,找到这个包里面所有的子模块,然后一个一个的把它们都导入进来。
但这个方法在 Windows 平台上工作的就不是非常好,因为 Windows 是一个不区分大小写的系统。
在 Windows 平台上,我们无法确定一个叫做 ECHO.py 的文件导入为模块是 echo 还是 Echo,或者是 ECHO。
为了解决这个问题,我们只需要提供一个精确包的索引。
导入语句遵循如下规则:如果包定义文件 init.py 存在一个叫做 all 的列表变量,那么在使用 from package import * 的时候就把这个列表中的所有名字作为包内容导入。
作为包的作者,可别忘了在更新包之后保证 all 也更新了啊。
以下实例在 file:sounds/effects/init.py 中包含如下代码:
__all__ = ["echo", "surround", "reverse"]
这表示当你使用from sound.effects import *这种用法时,你只会导入包里面这三个子模块。
如果 all 真的没有定义,那么使用**from sound.effects import ***这种语法的时候,就不会导入包 sound.effects 里的任何子模块。他只是把包sound.effects和它里面定义的所有内容导入进来(可能运行__init__.py里定义的初始化代码)。
这会把 init.py 里面定义的所有名字导入进来。并且他不会破坏掉我们在这句话之前导入的所有明确指定的模块。看下这部分代码:
import sound.effects.echo
import sound.effects.surround
from sound.effects import *
这个例子中,在执行 from…import 前,包 sound.effects 中的 echo 和 surround 模块都被导入到当前的命名空间中了。(当然如果定义了 all 就更没问题了)
通常我们并不主张使用 * 这种方法来导入模块,因为这种方法经常会导致代码的可读性降低。不过这样倒的确是可以省去不少敲键的功夫,而且一些模块都设计成了只能通过特定的方法导入。
记住,使用 from Package import specific_submodule 这种方法永远不会有错。事实上,这也是推荐的方法。除非是你要导入的子模块有可能和其他包的子模块重名。
如果在结构中包是一个子包(比如这个例子中对于包sound来说),而你又想导入兄弟包(同级别的包)你就得使用导入绝对的路径来导入。比如,如果模块sound.filters.vocoder 要使用包 sound.effects 中的模块 echo,你就要写成 from sound.effects import echo。
from . import echo
from .. import formats
from ..filters import equalizer
无论是隐式的还是显式的相对导入都是从当前模块开始的。主模块的名字永远是"main",一个Python应用程序的主模块,应当总是使用绝对路径引用。
包还提供一个额外的属性__path__。这是一个目录列表,里面每一个包含的目录都有为这个包服务的__init__.py,你得在其他__init__.py被执行前定义哦。可以修改这个变量,用来影响包含在包里面的模块和子包。
这个功能并不常用,一般用来扩展包里面的模块。
read_info’, ‘version’, ‘version_info’, ‘warnoptions’]
如果没有给定参数,那么 dir() 函数会罗列出当前定义的所有名称:
```python
>>> a = [1, 2, 3, 4, 5]
>>> import fibo
>>> fib = fibo.fib
>>> dir() # 得到一个当前模块中定义的属性列表
['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
>>> a = 5 # 建立一个新的变量 'a'
>>> dir()
['__builtins__', '__doc__', '__name__', 'a', 'sys']
>>>
>>> del a # 删除变量名a
>>>
>>> dir()
['__builtins__', '__doc__', '__name__', 'sys']
>>>
Python 本身带着一些标准的模块库,在 Python 库参考文档中将会介绍到(就是后面的"库参考文档")。
有些模块直接被构建在解析器里,这些虽然不是一些语言内置的功能,但是他却能很高效的使用,甚至是系统级调用也没问题。
这些组件会根据不同的操作系统进行不同形式的配置,比如 winreg 这个模块就只会提供给 Windows 系统。
应该注意到这有一个特别的模块 sys ,它内置在每一个 Python 解析器中。变量 sys.ps1 和 sys.ps2 定义了主提示符和副提示符所对应的字符串:
>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print('Runoob!')
Runoob!
C>
包是一种管理 Python 模块命名空间的形式,采用"点模块名称"。
比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B 。
就好像使用模块的时候,你不用担心不同模块之间的全局变量相互影响一样,采用点模块名称这种形式也不用担心不同库之间的模块重名的情况。
这样不同的作者都可以提供 NumPy 模块,或者是 Python 图形库。
不妨假设你想设计一套统一处理声音文件和数据的模块(或者称之为一个"包")。
现存很多种不同的音频文件格式(基本上都是通过后缀名区分的,例如: .wav,:file:.aiff,:file:.au,),所以你需要有一组不断增加的模块,用来在不同的格式之间转换。
并且针对这些音频数据,还有很多不同的操作(比如混音,添加回声,增加均衡器功能,创建人造立体声效果),所以你还需要一组怎么也写不完的模块来处理这些操作。
这里给出了一种可能的包结构(在分层的文件系统中):
sound/ 顶层包
__init__.py 初始化 sound 包
formats/ 文件格式转换子包
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ 声音效果子包
__init__.py
echo.py
surround.py
reverse.py
...
filters/ filters 子包
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
在导入一个包的时候,Python 会根据 sys.path 中的目录来寻找这个包中包含的子目录。
目录只有包含一个叫做 init.py 的文件才会被认作是一个包,主要是为了避免一些滥俗的名字(比如叫做 string)不小心的影响搜索路径中的有效模块。
最简单的情况,放一个空的 :file:init.py就可以了。当然这个文件中也可以包含一些初始化代码或者为(将在后面介绍的) __all__变量赋值。
用户可以每次只导入一个包里面的特定模块,比如:
import sound.effects.echo
这将会导入子模块:sound.effects.echo。 他必须使用全名去访问:
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
还有一种导入子模块的方法是:
from sound.effects import echo
这同样会导入子模块: echo,并且他不需要那些冗长的前缀,所以他可以这样使用:
echo.echofilter(input, output, delay=0.7, atten=4)
还有一种变化就是直接导入一个函数或者变量:
from sound.effects.echo import echofilter
同样的,这种方法会导入子模块: echo,并且可以直接使用他的 echofilter() 函数:
echofilter(input, output, delay=0.7, atten=4)
注意当使用 from package import item 这种形式的时候,对应的 item 既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量。
import 语法会首先把 item 当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,抛出一个 :exc:ImportError 异常。
反之,如果使用形如 import item.subitem.subsubitem 这种导入形式,除了最后一项,都必须是包,而最后一项则可以是模块或者是包,但是不可以是类,函数或者变量的名字。
如果我们使用 from sound.effects import * 会发生什么呢?
Python 会进入文件系统,找到这个包里面所有的子模块,然后一个一个的把它们都导入进来。
但这个方法在 Windows 平台上工作的就不是非常好,因为 Windows 是一个不区分大小写的系统。
在 Windows 平台上,我们无法确定一个叫做 ECHO.py 的文件导入为模块是 echo 还是 Echo,或者是 ECHO。
为了解决这个问题,我们只需要提供一个精确包的索引。
导入语句遵循如下规则:如果包定义文件 init.py 存在一个叫做 all 的列表变量,那么在使用 from package import * 的时候就把这个列表中的所有名字作为包内容导入。
作为包的作者,可别忘了在更新包之后保证 all 也更新了啊。
以下实例在 file:sounds/effects/init.py 中包含如下代码:
__all__ = ["echo", "surround", "reverse"]
这表示当你使用from sound.effects import *这种用法时,你只会导入包里面这三个子模块。
如果 all 真的没有定义,那么使用**from sound.effects import ***这种语法的时候,就不会导入包 sound.effects 里的任何子模块。他只是把包sound.effects和它里面定义的所有内容导入进来(可能运行__init__.py里定义的初始化代码)。
这会把 init.py 里面定义的所有名字导入进来。并且他不会破坏掉我们在这句话之前导入的所有明确指定的模块。看下这部分代码:
import sound.effects.echo
import sound.effects.surround
from sound.effects import *
这个例子中,在执行 from…import 前,包 sound.effects 中的 echo 和 surround 模块都被导入到当前的命名空间中了。(当然如果定义了 all 就更没问题了)
通常我们并不主张使用 * 这种方法来导入模块,因为这种方法经常会导致代码的可读性降低。不过这样倒的确是可以省去不少敲键的功夫,而且一些模块都设计成了只能通过特定的方法导入。
记住,使用 from Package import specific_submodule 这种方法永远不会有错。事实上,这也是推荐的方法。除非是你要导入的子模块有可能和其他包的子模块重名。
如果在结构中包是一个子包(比如这个例子中对于包sound来说),而你又想导入兄弟包(同级别的包)你就得使用导入绝对的路径来导入。比如,如果模块sound.filters.vocoder 要使用包 sound.effects 中的模块 echo,你就要写成 from sound.effects import echo。
from . import echo
from .. import formats
from ..filters import equalizer
无论是隐式的还是显式的相对导入都是从当前模块开始的。主模块的名字永远是"main",一个Python应用程序的主模块,应当总是使用绝对路径引用。
包还提供一个额外的属性__path__。这是一个目录列表,里面每一个包含的目录都有为这个包服务的__init__.py,你得在其他__init__.py被执行前定义哦。可以修改这个变量,用来影响包含在包里面的模块和子包。
这个功能并不常用,一般用来扩展包里面的模块。