Python基础

函 数

ord():函数获取字符的整数表示

chr():函数把编码转换为对应的字符

len():str包含多少个字符,函数计算的是str的字符数,如果换成bytes,len()函数就计算字节数

input():返回的数据类型是str,str不能直接和整数比较

range():可以生成一个整数序列=>range()函数,可以生成一个整数序列,再通过list()函数可以转换为list

int():函数来完成这件事情

sort():排序

math.sqrt() : 计算平方根

十六进制写str '\u4e2d\u6587' => '中文'

字 节 转 码

encode 转为字节bytes

由于Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。

Python对bytes类型的数据用带b前缀的单引号或双引号表示:

x = b'ABC'

要注意区分'ABC'和b'ABC',前者是str,后者虽然内容显示得和前者一样,但bytes的每个字符都只占用一个字节。

以Unicode表示的str通过encode()方法可以编码为指定的bytes

>>> 'ABC'.encode('ascii')

b'ABC'

>>> '中文'.encode('utf-8')

b'\xe4\xb8\xad\xe6\x96\x87'

>>> '中文'.encode('ascii')

Traceback (most recent call last):

File "", line 1, in

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

decode:bytes字节流转为str

如果我们从网络或磁盘上读取了字节流,那么读到的数据就是bytes。要把bytes变为str,就需要用decode()方法

>>> b'ABC'.decode('ascii')

'ABC'

>>>b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')

'中文'

如果bytes中包含无法解码的字节,decode()方法会报错

如果bytes中只有一小部分无效的字节,可以传入errors='ignore'忽略错误的字节:

>>>b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore')

'中'

由于Python源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按UTF-8编码读取,我们通常在文件开头写上这两行:

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释;

第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。

申明了UTF-8编码并不意味着你的.py文件就是UTF-8编码的,必须并且要确保文本编辑器正在使用UTF-8 without BOM编码

格 式 化 输 出

1)%格式化:

%d整数

%f浮点数

%s字符串

%x十六进制整数

例:

>>> 'Hello, %s' % 'world'

'Hello, world'

>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)

'Hi, Michael, you have $1000000.'

格式化整数和浮点数还可以指定是否补0和整数与小数的位数:

print('%5d-%05d' % (3, 1))

print('%.2f' % 3.1415926)

2)format()格式化:

另一种格式化字符串的方法是使用字符串的format()方法,它会用传入的参数依次替换字符串内的占位符、……,不过这种方式写起来比%要麻烦得多

list & tuple

列表LIST

len()

append()

insert()

pop()

classmates[1] = 'Sarah'

元组TUPLE

没有append(),insert()这样的方法。其他获取元素的方法和list是一样的,你可以正常地使用classmates[0],classmates[-1],但不能赋值成另外的元素

tuple不可变,所以代码更安全。如果可能,能用tuple代替list就尽量用tuple

tuple陷阱:

当你定义一个tuple时,在定义的时候,tuple的元素就必须被确定下来,比如:

>>> t = (1, 2)

>>> t

(1, 2)

如果要定义一个空的tuple,可以写成():

>>> t = ()

但是,要定义一个只有1个元素的tuple,如果你这么定义:

>>> t = (1)

>>> t

1

定义的不是tuple,是1这个数!这是因为括号()既可以表示tuple,又可以表示数学公式中的小括号,这就产生了歧义,因此,Python规定,这种情况下,按小括号进行计算,计算结果自然是1。

所以,只有1个元素的tuple定义时必须加一个逗号,,来消除歧义:

>>> t = (1,)

一个“可变的”tuple:

>>> t = ('a', 'b', ['A', 'B'])

>>> t[2][0] = 'X'

>>> t[2][1] = 'Y'

>>> t

('a', 'b', ['X', 'Y'])

tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向'a',就不能改成指向'b',指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的

循 环

1)迭代/for循环

for ... in ...

2)while循环

break

continue

字 典dict

类比php关联数组

查:

如果key不存在,dict就会报错,如何避免?

一是通过in判断key是否存在:

>>> 'Thomas' in d

False

二是通过dict提供的get()方法,如果key不存在,可以返回None,或者自己指定的value:

>>> d.get('Thomas')

>>> d.get('Thomas', -1)

-1

删:

pop(key)方法,对应的value也会从dict中删除

注:

1)dict内部存放的顺序和key放入的顺序是没有关系的

2)和list比较,dict有以下几个特点:

查找和插入的速度极快,不会随着key的增加而变慢;

需要占用大量的内存,内存浪费多。

list相反:

查找和插入的时间随着元素的增加而增加;

占用空间小,浪费内存很少。

3)dict的key必须是不可变对象。

集 合set

set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。

1)创建一个set:需要提供一个list作为输入集合

>>> s = set([1, 2, 3])

>>> s

注意,传入的参数[1, 2, 3]是一个list,而显示的只是告诉你这个set内部有1,2,3这3个元素,显示的顺序也不表示set是有序的。。

2)重复元素在set中自动被过滤:

>>> s = set([1, 1, 2, 2, 3, 3])

>>> s

3)通过add(key)方法可以添加元素到set中,可以重复添加,但不会有效果:

>>> s.add(4)

>>> s

>>> s.add(4)

>>> s

4)通过remove(key)方法可以删除元素:

>>> s.remove(4)

>>> s

5)set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作:

>>> s1 = set([1, 2, 3])

>>> s2 = set([2, 3, 4])

>>> s1 & s2

>>> s1 | s2

不可变对象

1)str是不变对象,而list是可变对象

对于可变对象,比如list,对list进行操作,list内部的内容是会变化的,比如:

>>> a = ['c', 'b', 'a']

>>> a.sort()

>>> a

['a', 'b', 'c']

2)对于不变对象来说,调用对象自身的任意方法,也不会改变该对象自身的内容。相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可变的

对于不可变对象,比如str,对str进行操作呢:

>>> a = 'abc'

>>> a.replace('a', 'A')

'Abc'

>>> a

'abc'

虽然字符串有个replace()方法,也确实变出了'Abc',但变量a最后仍是'abc',应该怎么理解呢?

我们先把代码改成下面这样:

>>> a = 'abc'

>>> b = a.replace('a', 'A')

>>> b

'Abc'

>>> a

'abc'

我们调用a.replace('a', 'A')时,实际上调用方法replace是作用在字符串对象'abc'上的,而这个方法虽然名字叫replace,但却没有改变字符串'abc'的内容。相反,replace方法创建了一个新字符串'Abc'并返回,如果我们用变量b指向该新字符串,就容易理解了,变量a仍指向原有的字符串'abc',但变量b却指向新字符串'Abc'了

函 数

1)函数参数:

一是必选参数在前,默认参数在后,否则Python的解释器会报错(思考一下为什么默认参数不能放在必选参数前面)

二是如何设置默认参数。当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。使用默认参数有什么好处?最大的好处是能降低调用函数的难度。

def enroll(name, gender, age=6, city='Beijing'):

print('name:', name)

print('gender:', gender)

print('age:', age)

print('city:', city)

有多个默认参数时,调用的时候,既可以按顺序提供默认参数,比如调用enroll('Bob','M',7),意思是,除了name,gender这两个参数外,最后1个参数应用在参数age上,city参数由于没有提供,仍然使用默认值。

也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时,需要把参数名写上。比如调用enroll('Adam', 'M', city='Tianjin'),意思是,city参数用传进去的值,其他默认参数继续使用默认值。

2)默认参数有个最大的坑,演示:

定义一个函数,传入一个list,添加一个END再返回:

def add_end(L=[]):

L.append('END')

return L

当你正常调用时,结果似乎不错:

>>> add_end([1, 2, 3])

[1, 2, 3, 'END']

>>> add_end(['x', 'y', 'z'])

['x', 'y', 'z', 'END']

当你使用默认参数调用时,一开始结果也是对的:

>>> add_end()

['END']

但是,再次调用add_end()时,结果就不对了:

>>> add_end()

['END', 'END']

>>> add_end()

['END', 'END', 'END']

很多初学者很疑惑,默认参数是[],但是函数似乎每次都“记住了”上次添加了'END'后的list。

原因解释如下:

Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。

定义默认参数要牢记一点:默认参数必须指向不变对象!

要修改上面的例子,我们可以用None这个不变对象来实现:

def add_end(L=None):

if L is None:

L = []

L.append('END')

return L

为什么要设计str、None这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。

3)可变参数:

def calc(*numbers):

sum = 0

for n in numbers:

sum = sum + n * n

return sum

在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数;

如果已经有一个list或者tuple,要调用一个可变参数怎么办?可以这样做:

>>> nums = [1, 2, 3]

>>> calc(nums[0], nums[1], nums[2])

14

这种写法当然是可行的,问题是太繁琐,所以Python允许你在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去:

>>> nums = [1, 2, 3]

>>> calc(*nums)

14

*nums表示把nums这个list的所有元素作为可变参数传进去。这种写法相当有用,而且很常见。

4)关键字参数

def person(name, age, **kw):

print('name:', name, 'age:', age, 'other:', kw)

函数person除了必选参数name和age外,还接受关键字参数kw。在调用该函数时,可以只传入必选参数:

>>> person('Michael', 30)

name: Michael age: 30 other: {}

也可以传入任意个数的关键字参数:

>>> person('Bob', 35, city='Beijing')

name: Bob age: 35 other: {'city': 'Beijing'}

>>> person('Adam', 45, gender='M', job='Engineer')

name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}

关键字参数有什么用?它可以扩展函数的功能。比如,在person函数里,我们保证能接收到name和age这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。

和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去:

>>> extra = {'city': 'Beijing', 'job': 'Engineer'}

>>> person('Jack', 24, city=extra['city'], job=extra['job'])

name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

当然,上面复杂的调用可以用简化的写法:

>>> extra = {'city': 'Beijing', 'job': 'Engineer'}

>>> person('Jack', 24, **extra)

name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

**extra表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数,kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra。

5)命名关键字参数

例如,只接收city和job作为关键字参数。这种方式定义的函数如下:

def person(name, age, *, city, job):

print(name, age, city, job)

和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。

调用方式如下:

>>> person('Jack', 24, city='Beijing', job='Engineer')

Jack 24 Beijing Engineer

如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:

def person(name, age, *args, city, job):

print(name, age, args, city, job)

命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错

6)参数组合

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)

在函数调用的时候,Python解释器自动按照参数位置和参数名把对应的参数传进去。

>>> f1(1, 2)

a = 1 b = 2 c = 0 args = () kw = {}

>>> f1(1, 2, c=3)

a = 1 b = 2 c = 3 args = () kw = {}

>>> f1(1, 2, 3, 'a', 'b')

a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}

>>> f1(1, 2, 3, 'a', 'b', x=99)

a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}

>>> f2(1, 2, d=99, ext=None)

a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}

最神奇的是通过一个tuple和dict,你也可以调用上述函数:

>>> args = (1, 2, 3, 4)

>>> kw = {'d': 99, 'x': '#'}

>>> f1(*args, **kw)

a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}

>>> args = (1, 2, 3)

>>> kw = {'d': 88, 'x': '#'}

>>> f2(*args, **kw)

a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}

对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的

小结:

Python的函数具有非常灵活的参数形态,既可以实现简单的调用,又可以传入非常复杂的参数。

默认参数一定要用不可变对象,如果是可变对象,程序运行时会有逻辑错误!

要注意定义可变参数和关键字参数的语法:

*args是可变参数,args接收的是一个tuple;

**kw是关键字参数,kw接收的是一个dict。

以及调用函数时如何传入可变参数和关键字参数的语法:

可变参数既可以直接传入:func(1, 2, 3),又可以先组装list或tuple,再通过*args传入:func(*(1, 2, 3));

关键字参数既可以直接传入:func(a=1, b=2),又可以先组装dict,再通过**kw传入:func(**{'a': 1, 'b': 2})。

使用*args和**kw是Python的习惯写法,当然也可以用其他参数名,但最好使用习惯用法。

命名的关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。

定义命名的关键字参数在没有可变参数的情况下不要忘了写分隔符*,否则定义的将是位置参数。

高 级 特 性

赋值语句:

a, b = b, a + b

相当于:

t = (b, a + b) # t是一个tuple

a = t[0]

b = t[1]

切片:(list||tuple)

L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3。即索引0,1,2,正好是3个元素。

如果第一个索引是0,还可以省略

倒数几个:

L[-2:]

前10个数,每两个取一个:

L[0:10:2]

迭代:(可迭代对象:str|dict|list|tuple)

迭代dict:

迭代key:

for key in d

迭代value:

for value in d.values()

迭代key和value:

for key,value in d.items()

迭代list:(key->val)

enumerate函数可以把一个list变成索引-元素对

列表生成式:

list(range(1, 11))

[x * x for x in range(1, 11)]

[x * x for x in range(1, 11) if x % 2 == 0]

[m + n for m in 'ABC' for n in 'XYZ']

[s.lower() for s in L]

生成器:generator

方法1:只要把一个列表生成式的[]改成(),next()函数获得generator的下一个返回值(一般不用),正确的方法是使用for循环,因为generator也是可迭代对象,通过for循环来迭代它,并且不需要关心StopIteration的错误。

(x * x for x in range(10))

方法2:yield(函数):

用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中

迭代器:

可以直接作用于for循环的数据类型有以下几种:

一类是集合数据类型,如list、tuple、dict、set、str等;

一类是generator,包括生成器和带yield的generator function。

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator,它们表示一个惰性计算的序列;

这些可以直接作用于for循环的对象统称为可迭代对象:Iterable

可以使用isinstance()判断一个对象是否是Iterable对象

list、dict、str等Iterable变成Iterator可以使用iter()函数

为什么list、dict、str等数据类型不是Iterator?

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181120G1XDN600?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券