由于在深度学习的路上,发现自己两年前学习的python有些遗忘,在面向对象这一块尤其不熟悉,故刷一遍廖雪峰老师的官方教程,梳理一下遗漏的知识点。
参考网址:https://www.liaoxuefeng.com/wiki/1016959663602400
1.默认字符不转义
如果字符串里面有很多字符都需要转义,就需要加很多\,为了简化,Python还允许用r’‘表示’'内部的字符串默认不转义。
demo1:
print('\\\t\\')
print(r'\\\t\\')#Python允许用r''表示''内部的字符串默认不转义
out1:
\ \
\\\t\\
2.除了\n的换行
demo2:
print('''line1
line2
line3''')
out2:
line1
line2
line3
3.字符编码问题
在这里插入代码片
ord()函数获取字符的整数表示;
chr()函数把编码转换为对应的字符。
demo3:
print(ord('A'))
print(ord('邓'))
print(chr(66))#不用加引号
print(chr(20216))
print('\u4e2d\u6587')
out3:
65
37011
B
仸
中文
由于Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。
Python对bytes类型的数据用带b前缀的单引号或双引号表示。
demo4:
>>> 'ABC'.encode('ascii')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
>>> '中文'.encode('ascii')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
纯英文的str可以用ASCII编码为bytes,内容是一样的,含有中文的str可以用UTF-8编码为bytes。含有中文的str无法用ASCII编码,因为中文编码的范围超过了ASCII编码的范围,Python会报错。
反过来,如果我们从网络或磁盘上读取了字节流,那么读到的数据就是bytes。要把bytes变为str,就需要用decode()方法。
demo5:
>>> b'ABC'.decode('ascii')
'ABC'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'
#如果bytes中只有一小部分无效的字节,可以传入errors='ignore'忽略错误的字节
>>> b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore')
'中'
4.len()函数
len()函数计算的是str的字符数,如果换成bytes,len()函数就计算字节数。
demo6:
a = len(b'ABC')
print(a)
b = len(b'\xe4\xb8\xad\xe6\x96\x87')
print(b)
c = len('中文'.encode('utf-8'))#转为byte
print(c)
out6:
3
6
6
1个中文字符经过UTF-8编码后通常会占用3个字节,而1个英文字符只占用1个字节。
5.字符串的格式化
demo7:
print('Hi, I am your %s' % 'father')# %s永远起作用,可以把任何数据类型转换为字符串
print('Hi, I am %d years old' % 22)#整数不用加引号
print('I have %d %s' % (5, 'apple'))#多个变量
print('%.2f' % 3.1415926) #格式化输出
print( 'growth rate: %d %%' % 7)#%是一个普通字符则需要转义,用%%来表示一个%
out7:
Hi, I am your father
Hi, I am 22 years old
I have 5 apple
3.14
growth rate: 7 %
f-string的方法:
使用以f开头的字符串,称之为f-string,它和普通字符串不同之处在于,字符串如果包含{xxx},就会以对应的变量替换.
demo8:
#python 3.6以上才支持这种方法
r = 2.5
s = 3.14 * r ** 2
print(f'The area of a circle with radius {r} is {s:.2f}')
out8:
The area of a circle with radius 2.5 is 19.62
6.删除列表中的元素
demo9:
li = ['a', 'b', 'c', 'd']
li.pop(2)#删除索引位置为2的元素,即c;不指定则删除最后一个元素
print(li)
out9:
['a', 'b', 'd']
7.定义只有一个元素的tuple
demo10:
t1 = (1)
print(t1)
t2 = (1,)#必须加逗号,不然默认小括号运算
print(t2)
out10:
1
(1,)
注意:tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向’a’,就不能改成指向’b’,指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的。
8.set
set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。
经常用来元素去重。
要创建一个set,需要提供一个list作为输入集合
demo11:
#创建一个set
s1 = set([1, 2, 3])
print(s1)
#set中无重复元素,重复元素自动过滤
s2 = set([1, 1, 2, 2, 3, 3])
print(s2)
#通过add(key)添加元素
s1.add(4)
print(s1)
#通过remove(key)删除元素
s1.remove(1)
print(s1)
out11:
{1, 2, 3}
{1, 2, 3}
{1, 2, 3, 4}
{2, 3, 4}
9.函数取别名
函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”。
demo12:
a = abs # 变量a指向abs函数
res = a(-1) # 所以也可以通过a调用abs函数
print(res)
out12:
1
10.整数转十六进制
demo13:
n1 = 255
n2 = 1000
print(hex(n1))
print(hex(n2))
out13:
0xff
0x3e8
11.空函数
如果想定义一个什么事也不做的空函数,可以用pass语句。pass可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来。
或者用在if条件判断语句中
demo14:
def nop():
pass
12.函数的默认参数
默认参数可以简化函数的调用。使得输入一个参数也可行,而不会报错。
注意:
1、必选参数在前,默认参数在后,否则Python的解释器会报错;
2、当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。
demo15:
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
print(power(5))
print(power(5, 3))
out15:
25
125
另外:默认参数必须指向不变对象!
demo16:
def add_end(L=[]):
L.append('END')
return L
#正常调用
res1 = add_end([1, 2, 3])
print(res1)
#使用默认参数
print(add_end())
print(add_end())
print(add_end())
#Python函数在定义的时候,默认参数L的值就被计算出来了,即[],
#因为默认参数L也是一个变量,它指向对象[],
#每次调用该函数,如果改变了L的内容,
#则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了
#修改如下:
def new_add_end(L=None):#使用None这个不变对象
if L is None:
L = []
L.append('END')
return L
print('-' * 50)
print(new_add_end())
print(new_add_end())
print(new_add_end())
out16:
[1, 2, 3, 'END']
['END']
['END', 'END']
['END', 'END', 'END']
--------------------------------------------------
['END']
['END']
['END']
13.函数的可变参数
在参数前面加一个*号。在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数。
demo17:
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
print(calc(1, 2))
print(calc())
#传入的本身就是个list或者tuple
nums = [1, 2, 3]
print(calc(*nums))#*nums表示把nums这个list的所有元素作为可变参数传进去
out17:
5
0
14
14.函数的关键词参数
可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
demo18:
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
#只传入必选参数
res1 = person('Michael', 30)
print(res1)
#传入任意个数的关键字参数
res2 = person('Bob', 35, city='Beijing')
print(res2)
res3 = person('Adam', 45, gender='M', job='Engineer')
print(res3)
#传入的本身就是一个字典
extra = {'city': 'Beijing', 'job': 'Engineer'}
res4 = person('Jack', 24, **extra)
print(res4)
'''
**extra表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数,
kw将获得一个dict,
注意kw获得的dict是extra的一份拷贝,
对kw的改动不会影响到函数外的extra.
'''
其实不用先赋值给res,再打印。
因为函数本身就有打印输出,而由于函数无return返回值,打印出来只会是None
out18:
name: Michael age: 30 other: {}
None
name: Bob age: 35 other: {'city': 'Beijing'}
None
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
None
name: Jack age: 24 other: {'job': 'Engineer', 'city': 'Beijing'}
None
命名关键词:限制关键字参数的名字。
demo19:
def person(name, age, *, city, job): # 只接收city和job作为关键字参数,*后面的参数被视为命名关键字参数
print(name, age, city, job)
#调用
person('Jack', 24, city='Beijing', job='Engineer')
#如果函数定义中已经有了一个可变参数
#后面跟着的命名关键字参数就不再需要一个特殊分隔符*
def person_v2(name, age, *args, city, job):
print(name, age, args, city, job)
#命名关键字参数必须传入参数名,不然报错
person_v2('Jack', 24, city = 'Beijing', job = 'Engineer')
#命名关键字参数可以有缺省值,从而简化调用
def person_v3(name, age, *, city='Shenzhen', job):
print(name, age, city, job)
person_v3('Jack', 24, job='Engineer')
out19:
Jack 24 Beijing Engineer
Jack 24 () Beijing Engineer
Jack 24 Shenzhen Engineer
用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个作为特殊分隔符。如果缺少,Python解释器将无法识别位置参数和命名关键字参数。
15.参数组合问题
参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
demo20:
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
#只用到了必选参数
f1(1, 2)
#必选参数和默认参数
f1(1, 2, c=3)
#必选、默认、可变参数(本例中为a和b两个)
f1(1, 2, 3, 'a', 'b')
#必选、默认、可变、关键词参数(x=99)
f1(1, 2, 3, 'a', 'b', x=99)
#必选,默认没给参数为默认的0,命名关键词
f2(1, 2, d=99, ext=None)
#通过一个tuple和dict,可以调用上述函数
args1 = (1, 2, 3, 4)
kw1 = {'d': 99, 'x': '#'}
f1(*args1, **kw1)
args2 = (1, 2, 3)
kw2 = {'d': 88, 'x': '#'}
f2(*args2, **kw2)
out20:
a = 1 b = 2 c = 0 args = () kw = {}
a = 1 b = 2 c = 3 args = () kw = {}
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}
a = 1 b = 2 c = 3 args = (4,) kw = {'x': '#', 'd': 99}
a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}
虽然可以组合多达5种参数,但不要同时使用太多的组合,否则函数接口的可理解性很差。
16.递归函数
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。
尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
demo21:
def fact(n):
return fact_iter(n, 1)
def fact_iter(num, product):
if num == 1:
return product
return fact_iter(num - 1, num * product)
out21:
#fact(5)对应的fact_iter(5, 1)的调用如下:
===> fact_iter(5, 1)
===> fact_iter(4, 5)
===> fact_iter(3, 20)
===> fact_iter(2, 60)
===> fact_iter(1, 120)
===> 120