python基础:
目录
数据类型和变量
浮点数
可以用数学写:1.23,-2.34等,也可以用科学计数法表示:一个浮点数的小数点位置是可变的,例如 和
因为太小了,看成完全相等的。对于很大或很小的浮点数,就必须用科学计数法表示,把10用e替代,
就是1.23e9。
字符串
以单引号 '
或双引号 "
括起来的任意文本。当用到 ' 或者 " 时用转义字符 \ 来表示。
- \n 表示换行
- \t 制表符
- \\ 表示的就是 \
>>> print('I\'m ok.')
I'm ok.
>>> print('I\'m learning\nPython.')
I'm learning
Python.
>>> print('\\\n\\')
\
\
如果字符串里面有很多字符都需要转义,可以用 r' ' 表示
>>> print('\\\t\\')
\ \
>>> print(r'\\\t\\')
\\\t\\
如果字符串内部有很多换行,用'''...'''
的格式表示多行内容
>>> print('''line1
... line2
... line3''')
line1
line2
line3
多行字符串'''...'''
还可以在前面加上r
使用:
>>>print(r'''hello,\n
world''')
hello,\n
world
>>> _
赋值
等号=
是赋值语句,可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,例如:
a = 123 # a是整数
print(a)
a = 'ABC' # a变为字符串
print(a)
#结果:
123
ABC
在赋值时Python解释器主要工作:1.在内存创建字符串 2.在内存中创建变量,并指向该字符串。
a = 'ABC'
b = a
a = 'XYZ'
print(b)
#结果:
ABC
对变量赋值 b=a 是把变量b指向变量a指向的真正的对象('ABC')。随后对变量a的赋值不影响变量b的指向
浮点数/整数运算
整数除法(地板除): 10 // 3 = 3
取余: 10 % 3 = 1
其余正常都是按浮点运算进行
如 9/ 3 = 3.0,结果是按浮点数运算
**用法
放在变量前,表示多少次方,如 2.0 ** 3.0,结果为8;功能类似于C语言中的 ^符号
- 放在参数前,表示入参为字典,如 dealit(x, **args),第二个输入参数需为字典
字符串和编码
bytes
类型的数据用带b
前缀的单引号或双引号表示:
x = b'ABC'
以Unicode表示的str
通过encode()
方法可以编码为指定的bytes:
>>> 'ABC'.encode('ascii')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
把bytes
变为str
,就需要用decode()
方法:
>>> b'ABC'.decode('ascii')
'ABC'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'
输入输出
在Python中,采用的格式化方式和C语言是一致的,用%
实现:
>>> 'Hello, %s' % 'world'
'Hello, world'
>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
'Hi, Michael, you have $1000000.'
常见的占位符有:
占位符 替换内容 %d 整数 %f 浮点数 %s 字符串 %x 十六进制整数 %% %
另一种格式化字符串的方法是使用字符串的format()
方法:
>>> 'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125)
'Hello, 小明, 成绩提升了 17.1%'
最后一种格式化字符串的方法是使用以f
开头的字符串,称之为f-string
,它和普通字符串不同之处在于,字符串如果包含{xxx}
,就会以对应的变量替换:
>>> r = 2.5
>>> s = 3.14 * r ** 2
>>> print(f'The area of a circle with radius {r} is {s:.2f}')
The area of a circle with radius 2.5 is 19.62
使用list和tuple
list:
是个数组列表,里面可以存浮点、整数、字符串等不同类型
list可以内部再嵌套list类型,实现多维数组
定义方式为num = []
初始化举例:classmates = ['Michael', 'Bob', 'Tracy']
>>> classmates = ['Michael', 'Bob', 'Tracy']
>>> classmates
['Michael', 'Bob', 'Tracy']
>>> classmates[0]
'Michael'
>>> classmates[1]
'Bob'
>>> classmates[2]
'Tracy'
>>> classmates[-1]
'Tracy'
常用操作:
- 取长度,len(),不仅限于list,可以针对任意对象
- 引用,xx[0], xx[-1]--指的是最后一个元素
- 追加,xx.append(‘’),往列表末尾插入对象,注意一次只能一个元素
- 插入,xx.insert(i, ‘’),往指定位置插入对象
- 删除末尾元素,xx.pop()
- 删除指定,xx.pop(i),删除指定位置i处的对象
- 排序,xx.sort(),默认按字典序或数值升序
tuple:
tuple也是有序列表叫元组,一旦初始化就不能修改,因此没有增删改操作,只有查操作
定义一个空的tuple:
>>> t = ()
>>> t
()
定义一个只有1个元素的tuple:
>>> t = (1,)
>>> t
(1,)
#注意下面错误的定义,这样定义定义的不是一个元组而是一个数
>>> t = (1)
>>> t
1
初始化举例:classmates = ('Michael', 'Bob', 'Tracy')
可变与不可变辨析
>>> t = ('a', 'b', ['A', 'B'])
>>> t[2][0] = 'X'
>>> t[2][1] = 'Y'
>>> t
('a', 'b', ['X', 'Y'])
表面上看,tuple的元素确实变了,但其实变的不是tuple的元素,而是list的元素。tuple一开始指向的list并没有改成别的list,所以,tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向'a'
,就不能改成指向'b'
,指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的!
>>> a = 'abc'
>>> a.replace('a', 'A')
'Abc'
>>> a
'abc'
replace方法会创建返回一个副本,a本身指向的内容不会变;
>>> a = 'abc'
>>> b = a.replace('a', 'A')
>>> b
'Abc'
>>> a
'abc'
条件判断:
if
语句的完整形式就是:
if <条件判断1>:
<执行1>
elif <条件判断2>:
<执行2>
elif <条件判断3>:
<执行3>
else:
<执行4>
input()
返回的数据类型是str。
int()
函数:强制转换为int类型。
s = input('birth: ')
birth = int(s)
if birth < 2000:
print('00前')
else:
print('00后')
循环:
for...in循环
names = ['Michael', 'Bob', 'Tracy']
for name in names:
print(name)
while循环
#计算100以内所有奇数之和
sum = 0
n = 99
while n > 0:
sum = sum + n
n = n - 2
print(sum)
break
break:提前结束循环。
n = 1
while n <= 100:
if n > 10: # 当n = 11时,条件满足,执行break语句
break # break语句会结束当前循环
print(n)
n = n + 1
print('END')
continue
continue:跳过当前的这次循环,直接开始下一次循环:
n = 0
while n < 10:
n = n + 1
if n % 2 == 0: # 如果n是偶数,执行continue语句
continue # continue语句会直接继续下一轮循环,后续的print()语句不会执行
print(n)
- range(i):生成0到 i-1 的整数序列
- list(range(i)):将该整数序列转为list
使用dict和set
dict
数据字典,key-value存储方式,key算出value的存放位置
# key :人名,vaule:成绩
>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
>>> d['Michael']
95
判断key存不存在的两种方法:
一是通过
in
判断key是否存在:
>>> 'Thomas' in d
False
二是通过dict提供的
get()
方法,如果key不存在,可以返回None
,或者自己指定的value:注意:返回
None
的时候Python的交互环境不显示结果。
>>> d.get('Thomas')
>>> d.get('Thomas', -1)
-1
d.pop(key):删除一个key,对应的value也会从dict中删除
set
set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。
创建set,需要提供一个list作为输入集合:
>>> s = set([1, 2, 3])
>>> s
{1, 2, 3}
重复元素在set中自动被过滤:
>>> s = set([1, 1, 2, 2, 3, 3])
>>> s
{1, 2, 3}
s.add(key):添加元素到set中,t添加相同的元素不会有效果,因为set中没有重复的key值。
s.remove(key):删除元素。
set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作:
>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s1 & s2
{2, 3}
>>> s1 | s2
{1, 2, 3, 4}
函数
调用函数:
- abs():求绝对值函数
- max():有任意个参数,返回最大的
- int(‘str’):字符串转成整数
- float(‘str’):字符串转成浮点值
- str(num):将数字转为字符串
- bool(elment):判断元素(可以为数字、字符串、list等类型)的真假
- hex(num):将十进制整数用十六进制输出
- isinstance(x, (int, float)):判断x元素的类型是不是int 或float,若果是,则为true,否则为false
- str.lower():将字符串str转为小写
- str.upper():将字符串str转为大写
定义函数
定义一个函数要使用def
语句,依次写出函数名、括号、括号中的参数和冒号:
,然后,在缩进块中编写函数体,函数的返回值用return
语句返回。
自定义一个求绝对值的my_abs
函数:
def my_abs(x):
if x >= 0:
return x
else:
return -x
空函数
定义一个什么事也不做的空函数,可以用pass
语句:
def nop():
pass
实际上
pass
可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass
,让代码能运行起来。
返回多个值
Python的函数返回多值其实就是返回一个tuple。
import math
def move(x, y, step, angle=0):
nx = x + step * math.cos(angle)
ny = y - step * math.sin(angle)
return nx, ny
>>> x, y = move(100, 100, 60, math.pi / 6)
>>> print(x, y)
151.96152422706632 70.0
看似获得了两个返回值,其实是单一值是个tuple。
>>> r = move(100, 100, 60, math.pi / 6)
>>> print(r)
(151.96152422706632, 70.0)
在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便。
小结
- 定义函数时,需要确定函数名和参数个数;
- 如果有必要,可以先对参数的数据类型做检查;
- 函数体内部可以用
return
随时返回函数结果;- 函数执行完毕也没有
return
语句时,自动return None
。- 函数可以同时返回多个值,但其实就是一个tuple。
函数的参数:
默认参数:
可以灵活的控制函数的入参,参数不够时,按默认配参输入
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
调用可以写:power(5),相当于调用power(5, 2),当n>2时,必须明确地传入n,比如power(5, 3)
设置默认参数时的注意事项:
- 必选参数在前,默认参数在后。
- 当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。
- 对于确定的参数输入,优先赋值给必选参数。
- 默认参数必须指向不变对象!
可变参数
本质是传入的参数是tuple
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
*numbers,表示将tuple指针也引用成每个元素
如果已经有一个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的所有元素作为可变参数传进去。
关键字参数
关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict,即有key-value映射。
实例:
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
>>> 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
。
命名关键字参数
如果要限制关键字参数的名字,就可以用命名关键字参数。,例如,只接收city
和job
作为关键字参数:
def person(name, age, *, city, job):
print(name, age, city, job)
和关键字参数
**kw
不同,命名关键字参数需要一个特殊分隔符*
,*
后面的参数被视为命名关键字参数。*后面的参数可以有可以没有,如果有的话必须说明是city还是job。
def person(name, age, *, city='Beijing', job):
print(name, age, city, job)
>>> person('Jack', 24, job='Engineer')
Jack 24 Beijing Engineer
使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个*
作为特殊分隔符。如果缺少*
,Python解释器将无法识别位置参数和命名关键字参数。
def person(name, age, city, job):
# 缺少 *,city和job被视为位置参数
pass
def person(name, age, *args, city, job): ## 参数可变
print(name, age, args, city, job)
参数组合
在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
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)
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}
*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})
。
对于任意函数,都可以通过类似func(*args, **kw)
的形式调用它,无论它的参数是如何定义的。
>>> 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': '#'}
递归函数:
在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。
高级特性
切片
在list/tuple中访问任意片段或间隔规律的元素,注意访问下标是左闭右开[left, right)
实例:
#先创建一个0-99的数列
>>> L = list(range(100))
>>> L
[0, 1, 2, 3, ..., 99]
#通过切片轻松取出某一段数列。比如前10个数:
>>> L[:10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
后10个数:
>>> L[-10:]
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
#前11-20个数:
>>> L[10:20]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
#前10个数,每两个取一个:
>>> L[:10:2]
[0, 2, 4, 6, 8]
#所有数,每5个取一个:
>>> L[::5]
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
#只写[:]就可以原样复制一个list:
>>> L[:]
[0, 1, 2, 3, ..., 99]
字符串'xxx'
也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串:
>>> 'ABCDEFG'[:3]
'ABC'
>>> 'ABCDEFG'[::2]
'ACEG'
小结:
s[:],显示所有元素
s[-1:],从-1到0,即显示最后一个元素
s[:10],从0开始到9,即显示前10个元素,下标为10的不显示
s[2:10:3],从下标2到10,每间隔3个,取一个元素
s[-10:-1],从-10个元素,显示至倒数第二个元素,-1是截止,且为开
迭代
定义:
如果给定一个list
或tuple
,我们可以通过for
循环来遍历这个list
或tuple
,这种遍历我们称为迭代(Iteration)。
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
... print(key)
...
a
b
c
字符串也是可迭代对象:
>>> for ch in 'ABC':
... print(ch)
...
A
B
C
如何判断一个对象是可迭代对象呢?方法是通过collections.abc
模块的Iterable
类型判断:
>>> from collections.abc import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False
列表生成式
列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。
写列表生成式时,把要生成的元素x * x
放到前面,后面跟for
循环,就可以把list创建出来:
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
增加判断如果只有if将if放到后面,如果有if和else时将if else放到前面:
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
>>> [x if x % 2 == 0 else -x for x in range(1, 11)]
[-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
在一个列表生成式中,
for
前面的if ... else
是表达式,而for
后面的if
是过滤条件,不能带else
。
可以使用两层循环,可以生成全排列:
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
生成器
定义:一边循环一边计算的机制,称为生成器:generator。生成器的本质是生成一个tuple。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个generator:
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
#如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
#也可以用for循环:
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)
...
0
1
4
9
16
25
36
49
64
81
方法二:yield声明
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
如果一个函数定义中包含
yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator函数,调用一个generator函数将返回一个generator。
generator函数和普通函数的执行流程不一样。普通函数是顺序执行,遇到
return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。
迭代器
直接作用于for
循环的数据类型有以下几种:
一类是集合数据类型,如list
、tuple
、dict
、set
、str
等;
一类是generator
,包括生成器和带yield
的generator function。
这些可以直接作用于for
循环的对象统称为可迭代对象:Iterable
。
可以被next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用isinstance()
判断一个对象是否是Iterator
对象:
>>> from collections.abc import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False
生成器都是Iterator
对象,但list
、dict
、str
虽然是Iterable
,却不是Iterator
。
把list
、dict
、str
等Iterable
变成Iterator
可以使用iter()
函数:
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
小结:
凡是可作用于
for
循环的对象都是Iterable
类型;凡是可作用于
next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列;集合数据类型如
list
、dict
、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象。
函数式编程:
高阶函数
变量可以指向函数
# 求-10的绝对值
print(abs(-10)) # 10
print(abs) # <built-in function abs>
# abs()对函数的调用,abs是函数本身
f = abs # f变量也是指向abs所指向的函数
# 可以用变量f来调用函数
print(f(-10))
# 结论:变量 f 指向了abs函数本身
函数名也是变量
函数名就是指向函数的变量。
# 函数名也是变量
abs = 10
print(abs) #10
print(abs(-10)) # 报错
报错的原因就是把abs这个函数名当成了变量,指向10,此时的abs就不再指向绝对值函数。
一个函数就可以接收另一个函数作为参数,这种函数就是高阶函数。
def add(x, y, f):
return f(x) + f(y)
print(add(-5, 6, abs)) # 11
map/reduce
map()
函数接收两个参数,第一个是函数,,后面的参数是一个或多个Iterable
,map
将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator
返回。
map函数的语法:
map(function, iterable, …)
#求f(x)=x^2 作用在list[1,2,3,4,5,6,7,8,9]
a=[1,2,3,4,5,6,7,8,9]
def f(x):
return x*x
rs=[]
for i in a:
rs.append(f(i))
print(rs) #[1, 4, 9, 16, 25, 36, 49, 64, 81]
#使用map实现:
rs1=map(f,a) #返回的rs1是一个可迭代对象
#判断是否是可迭代对象(可以省略,只是初学时的判断)
from collections.abc import Iterator
print('判断是否为可迭代的',isinstance(rs1,Iterator)) #true
print(list(rs1)) #[1, 4, 9, 16, 25, 36, 49, 64, 81]
利用map将列表中的每个元素转换为字符串::
a=[1,2,3,4,5,6,7,8,9]
s=map(str,a)
print(list(s))
map传递两个列表:
a=[1,2,3,4]
b=[10,20,30]
def f(x,y):
return x+y
L=map(f,a,b)
print(list(L))
reduce:
reduce把一个函数作用在一个序列[x1,x2,x3,x4...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果是:
reduce(f,[x1,x2,x3,x4])=f(f(f(x1,x2),x3),x4)
对一个序列求和:
from functools import reduce
def add(x,y):
return x+y
sum=reduce(add,[1,2,3,4,5])
print(sum) #15
把序列[1,2,3,4]变为整数1234:
def zh(x,y):
return x*10+y
print(reduce(zh,[1,2,3,4])) #1234
写出把str转换为int:
def fn(x,y):
return x*10+y
def char2num(s):
sz={'0':0,'1':1,'2':2,'3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
return sz[s]
print(reduce(fn,map(char2num,'12345'))) #12345
还可以用lambda函数进一步简化成:
from functools import reduce
def char2num(s):
sz={'0':0,'1':1,'2':2,'3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
return sz[s]
def str2int(s):
return reduce(lambda x, y: x * 10 + y, map(char2num, s))
print(str2int('12345'))
filter
filter()
函数用于过滤序列。和
map()
类似,filter()
也接收一个函数和一个序列。和map()
不同的是,filter()
把传入的函数依次作用于每个元素,然后根据返回值是True
还是False
决定保留还是丢弃该元素。
在一个list中,删掉偶数,只保留奇数:
def is_odd(l):
return l%2==1
print(list(filter(is_odd,[1,2,3,4,5,6,7,8,9])))
把一个序列中的空字符串删掉:
def no_empty(s):
return s and s.strip()
print(list(filter(no_empty,['A', '', 'B', None, 'C', ' '])))
需要注意的是:''和None都是空字符串但是' '是一个字符串,字符串的内容为空格。
sorted
对数大小进行比较:
print(sorted([23,32,4,56,-20]))
#输出结果:
[-20, 4, 23, 32, 56]
逆序排序,给sorted添加reverse参数:
print(sorted([23,32,4,56,-20],reverse=True))
#输出结果:
[56, 32, 23, 4, -20]
对字符串 利用ascll码 A:65 a:97
print(sorted(['ABC','D','C','d','abc']))
sorted是高阶函数,他还可以接收一个key函数来实现自定义的排序
对数值的列表按绝对值的大小进行排序
print(sorted([23,32,4,56,-20],key=abs))
#输出结果:
[4, -20, 23, 32, 56]
对字符串列表,忽略大小写
print(sorted(['ABC','D','C','d','abc'],reverse=True,key=str.lower))
print(sorted(['ABC','d','C','D','abc'],key=str.lower))
print(sorted(['ABC','D','C','d','abc'],key=str.lower))
#输出结果:
['D', 'd', 'C', 'ABC', 'abc']
['ABC', 'abc', 'C', 'd', 'D']
['ABC', 'abc', 'C', 'D', 'd']
返回函数
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
def lazy_sum(*args):
def sum(): #这是一个局部函数
ax = 0
for n in args:
ax = ax + n
return ax
return sum
#当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:
f = lazy_sum(1, 3, 5, 7, 9)
print(f) #<function lazy_sum.<locals>.sum at 0x000002996224A4C0>
#当调用函数f()时才计算求和的结果
print(f()) #25
在函数
lazy_sum
中又定义了函数sum
,并且,内部函数sum
可以引用外部函数lazy_sum
的参数和局部变量,当lazy_sum
返回函数sum
时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)。在上面实例中,sum()是一个闭包,因为他引用了lazy_sum的参数args。
如何创建闭包:
a.要有函数的嵌套(外部函数,内部函数)
b.内部函数中使用外部函数的变量
c.外部函数必须有返回值
调用lazy_sum()
时,每次调用都会返回一个新的函数,即使传入相同的参数:
f1 = lazy_sum(1, 3, 5, 7, 9)
f2 = lazy_sum(1, 3, 5, 7, 9)
print(f1) #<function lazy_sum.<locals>.sum at 0x00000160FE15A550>
print(f2) #<function lazy_sum.<locals>.sum at 0x00000160FE15A5E0>
f1()和f2()的调用结果互不影响。
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
例子:
使用闭包求两点之间的距离:
'''
两个点(x1,y1)(x2,y2)
距离:math.sqrt((x1-x2)**2+(y1-y2)**2)
'''
import math
def juli(x1,y1,x2,y2):
return math.sqrt((x1-x2)**2+(y1-y2)**2)
x1=float(input("请输入x1:"))
y1=float(input("请输入y1:"))
x2=float(input("请输入x2:"))
y2=float(input("请输入y2:"))
print('点1为:({0},{1}),点二为:({2},{3})'.format(x1,y1,x2,y2))
print("两点之间的距离是:",juli(x1,y1,x2,y2))
#使用闭包:
def wai(x1,y1):
def nei(x2,y2):
return math.sqrt((x1-x2)**2+(y1-y2)**2)
return nei
a=wai(0,0)
b=a(1,1)
print(b)
匿名函数
匿名函数的表达式:lambda:参数:表达式
匿名函数作为map高阶函数的参数 f(x)=x*x
l=map(lambda x:x*x,[1,2,3,4,5])
print(list(l)) #[1, 4, 9, 16, 25]
sorted中使用匿名函数:
class Student:
def __init__(self,name,age):
self.name=name
self.age=age
stu1=Student('zhangsan',21)
stu2=Student('lisi',25)
stu3=Student('wangwu',23)
result_list=sorted([stu1,stu2,stu3],key=lambda x:x.age)
for stu in result_list:
print('name:',stu.name,'age:',stu.age)
#输出结果:
name zhangsan age 21
name wangwu age 23
name lisi age 25
装饰器
当我们要在其中一个函数中增加新的功能时,但是又不希望修改函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
偏函数
Python的functools
模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。
偏函数是用于对函数固定属性的函数,作用就是把一个函数某些参数固定住(即默认值),返回一个新的函数,调用这个新的函数就很简单。
以int()函数把字符串转换为整数为例:
#base 默认值为10,传入base参数,可以做N进制的转换:
print('将字符串转换为八进制:',int('12345',base=8)) #将字符串转换为八进制: 5349
print('将字符串转换为十六进制:',int('12345',base=16)) #将字符串转换为十六进制: 74565
若需要转换大量的二进制字符串每次转入int(x,base=2)很麻烦,因此可以默认base=2传进去:
def int2(x):
return int(x,base=2)
print(int2('1010')) #10
print(int2('101010')) #42
print(int2('10101010')) #170
使用偏函数:首先需要导入functools这个包
from functools import partial
int2=partial(int,base=2) #partial(函数,默认的值)
print(int2('1010')) #10
print(int2('101010')) #42
print(int2('10101010')) #170
print(int2('10101010',base=10)) #10101010
小结:
当函数的参数个数太多,需要简化时,使用
functools.partial
可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。
模块
在Python中,一个.py文件就称之为一个模块(Module)。
模块是一组Python代码的集合,可以使用其他模块,也可以被其他模块使用。
创建自己的模块时,要注意:
- 模块名要遵循Python变量命名规范,不要使用中文、特殊字符;
- 模块名不要和系统模块名冲突,最好先查看系统是否已存在该模块,检查方法是在Python交互环境执行import abc,若成功则说明系统存在此模块。
使用模块
在使用模块之前首先是导入模块,导入模块之后就有了变量指向该模块,通过变量就可以访问模块的所有功能。导入模块用import 。
作用域:
- 正常的函数和变量名是公开的(public),可以被直接引用,比如:
abc
,x123
,PI
等;- 类似
__xxx__
这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author__
,__name__
就是特殊变量,我们自己的变量一般不要用这种变量名;- 类似
_xxx
和__xxx
这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc
,__abc
等;
总结:
- 在Python中,是通过
_
前缀来区分public和private函数,另外特殊的变量用两个 __ 来表示,例如__author__,__doc__, __name__等。 - 私有函数可以加前缀 __,表示不允许外部访问。
- 使用模块之前应该先导入。
安装第三方模块
第三方库都会在Python官方的pypi.python.org网站注册,要安装一个第三方库,必须先知道该库的名称,可以在官网或者pypi上搜索,比如Pillow的名称叫Pillow,因此,安装Pillow的命令就是:
>>>pip install Pillow
安装常用模块:
直接使用Anaconda,这是一个基于Python的数据处理和科学计算平台,它已经内置了许多非常有用的第三方库,我们装上Anaconda,就相当于把数十个第三方模块自动安装好了,非常简单易用。
面向对象编程
类和实例
面向对象最重要的概念就是类(Class)和实例(Instance),类是抽象的模板,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。
定义类是通过class关键字:
class Student(object):
pass
类名通常是大写开头的单词,紧接着是object,表示该类是从哪个类继承下来的,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。
创建实例是通过类名+()实现的:
class Student(object):
pass
#创建实例
st1=Student()
print(st1) #<__main__.Student object at 0x00000240E74C8400>
定义初始化方法:
class Student(object):
def __init__(self,name,chengji):
self.name=name
self.chengji=chengji
st1=Student('wangteng','70')
print(st1.name,st1.chengji)
注意到__init__
方法的第一个参数永远是self
,表示创建的实例本身,因此,在__init__
方法内部,就可以把各种属性绑定到self
,因为self
就指向创建的实例本身。
有了__init__
方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__
方法匹配的参数,但self
不需要传,Python解释器自己会把实例变量传进去。
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self
,并且,调用时,不用传递该参数。
数据封装
在类中定义方法,通过在实例上调用方法,就可以直接操作对象内部的数据,不需要知道方法内部的实现细节。
封装输出函数:
class Student(object):
def __init__(self,name,chengji):
self.name=name
self.chengji=chengji
def print_chengji(self):
print('%s: %s' % (self.name, self.chengji))
def get_chengji(self):
if self.chengji >= 90:
return 'A'
elif self.chengji >= 60:
return 'B'
else:
return 'C'
st1=Student('wangteng',70)
st1.print_chengji()
print(st1.name,st1.chengji,st1.get_chengji())
输出结果:
wangteng: 70
wangteng 70 B
和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同:
class Student(object):
def __init__(self,name,chengji):
self.name=name
self.chengji=chengji
def print_chengji(self):
print('%s: %s' % (self.name, self.chengji))
def get_chengji(self):
if self.chengji >= 90:
return 'A'
elif self.chengji >= 60:
return 'B'
else:
return 'C'
st1=Student('wangteng',70)
st1.print_chengji()
print(st1.name,st1.chengji,st1.get_chengji())
bart = Student('Bart Simpson', 59)
lisa = Student('Lisa Simpson', 87)
bart.age = 8
print(bart.age)
print(lisa.age)
#输出结果:
8
Traceback (most recent call last):
File "E:/pythonproject/pythonProject/面向对象编程/01类和实例.py", line 24, in <module>
print(lisa.age)
AttributeError: 'Student' object has no attribute 'age'
小结:
类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;
方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据;
通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。
访问限制
将变量隐藏起来,通过调用get set方法实现对变量的调用:
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
# getter/setter 方法
def get_name(self):
return self.__name
def set_name(self, name):
self.__name = name
def get_score(self):
return self.__score
def set_score(self, score):
if score >= 0 and score <= 100:
self.__score = score
else:
raise ValueError('illegal score')
st1 = Student('wangteng', 100)
#print(st1.__name) 报错
'''
Traceback (most recent call last):
File "E:/pythonproject/pythonProject/面向对象编程/02 访问限制.py", line 27, in <module>
print(st1.__name)
AttributeError: 'Student' object has no attribute '__name'
'''
print('%s: %s' % (st1.get_name(), st1.get_score())) # 打印:wangteng: 100
st1.set_name('wangteng1')
#st1.set_score(150) # 不合法的分数,抛异常
st1.set_score(99)
print('%s: %d' % (st1.get_name(), st1.get_score())) # 打印:wangteng1: 99
继承和多态
继承
子类的定义是:
def 子类名(父类名)
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
pass
class Cat(Animal):
pass
dog = Dog()
dog.run() # Animal is running...
cat = Cat()
cat.run() # Animal is running...
Animal是父类,Dog和Cat是子类,子类继承了父类的所有方法 。子类也可以添加自己的方法。
多态
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
def run(self):
print('Dog is running...')
class Cat(Animal):
def run(self):
print('Cat is running...')
class Tortoise(Animal):
def run(self):
print('Tortoise is running slowly...')
def run_twice(a):
a.run()
a.run()
run_twice(Animal())
run_twice(Dog())
run_twice(Cat())
run_twice(Tortoise())
#输出结果:
Animal is running...
Animal is running...
Dog is running...
Dog is running...
Cat is running...
Cat is running...
Tortoise is running slowly...
Tortoise is running slowly...
对于一个变量,我们只需要知道它是Animal
类型,无需确切地知道它的子类型,就可以放心地调用run()
方法,而具体调用的run()
方法是作用在Animal
、Dog
、Cat
还是Tortoise
对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal
的子类时,只要确保run()
方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:
对扩展开放:允许新增Animal
子类;
对修改封闭:不需要修改依赖Animal
类型的run_twice()
等函数。
动态语言vs静态语言
静态语言(例如Java):如果需要传入Animal
类型,则传入的对象必须是Animal
类型或者它的子类,否则,将无法调用run()
方法。
动态语言(python):则不一定需要传入Animal
类型。我们只需要保证传入的对象有一个run()
方法就可以了。
class Animal(object):
def run(self):
print('Animal is running...')
def run_twice(a):
a.run()
a.run()
class Timer(object):
def run(self):
print('Start...')
run_twice(Timer())
输出结果:
Start...
Start...
这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
获取对象信息
type()函数
用于基本类型数据:
print(type(123))
print(type('123'))
print(type(None))
#输出结果
<class 'int'>
<class 'str'>
<class 'NoneType'>
判断指向函数或类的变量的类型:
print(type(abs))
class Student(object):
pass
a=Student()
print(type(a))
输出结果:
<class 'builtin_function_or_method'>
<class '__main__.Student'>
判断 type()
函数作用的变量的类型是否相等:
print(type(123)==type(456))
print(type(123)==int)
print(type('123')==type('abc'))
print(type('123')==str)
print(type("123")==type(456))
输出结果:
True
True
True
True
False
判断一个对象是否是函数:
print(type(fn)==types.FunctionType)
print(type(abs)==types.BuiltinFunctionType)
print(type(lambda x: x)==types.LambdaType)
print(type((x for x in range(10)))==types.GeneratorType) #生成器类型
输出结果:
True
True
True
True
isinstance()
class Animal(object):
pass
class Dog(Animal):
pass
class Husky(Dog):
pass
f = Animal()
a = Dog()
h = Husky()
print(isinstance(f, Animal)) # True
print(isinstance(a, Animal)) # True
print(isinstance(h, Animal)) # True
print(isinstance(a, Husky)) # False
# isinstance 用于基本数据类型
print(isinstance(123, int)) # True
print(isinstance(True, bool)) # True
print(isinstance(3.14, float)) # True
print(isinstance('hello', str)) # True
# isinstance 判断一个变量是否某些类型中的一种
print(isinstance([1, 2, 3], (list, tuple))) # True
type() 不考虑继承关系,isinstance() 考虑继承关系,因此要判断两个类型是否相同一般使用 isinstance()。
dir()函数
dir()函数:获得一个对象的所有属性和方法。返回一个包含字符串的list。
print(dir('abc'))
输出结果:
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
获取一个对象的长度, 使用 len() 函数,内部调用对象的 __len__ 函数
print(len('ABC'))
print('ABC'.__len__())
输出结果:
3
3
若定义的类中也想调用len(myObj)也可以自己定义__len__()方法:
class wt(object):
def __len__(self):
return 100
wt1=wt()
print(len(wt1)) #100
print(wt1.__len__()) #100
配合getattr()
、setattr()
以及hasattr()
,操作一个对象的状态:
class wt(object):
def __len__(self):
return 100
wt1=wt()
print(len(wt1))
print(wt1.__len__())
class wtt(object):
def __init__(self):
self.x=9
def power(self):
return self.x*self.x
wt=wtt()
# 判断有无属性 x?
print(hasattr(wt, 'x')) # True
print(wt.x) # 9
# 判断有无属性 y?
print(hasattr(wt, 'y')) # False
# 设置一个属性 y?
setattr(wt, 'y', 19)
# 再判断有无属性 y?
print(hasattr(wt, 'y')) # True
# 获取属性 y
print(getattr(wt, 'y')) # 19
# 获取属性,如果不存在,返回默认值
print(getattr(wt, 'z', -1)) # -1
# 获取对象的方法
print(hasattr(wt, 'power')) # True
print(getattr(wt, 'power')) # <bound method wtt.power of <__main__.wtt object at 0x00000171F1014E20>>
fn = getattr(wt, 'power')
print(fn) # <bound method wtt.power of <__main__.wtt object at 0x00000171F1014E20>>
print(fn()) # 81
实例属性和类属性
# 实例属性和类属性
class Student(object):
# name 是类属性,归 Student 类所有
name = 'Student'
#创建实例
wt = Student()
# 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
print(wt.name) # Student
# 打印类的name属性
print(Student.name) # Student
# 给实例绑定name属性
wt.name='wangteng'
# 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
print(wt.name) #wangteng
# 类属性并未消失,用Student.name仍然可以访问
print(Student.name) # Student
#删除实例的name属性
del wt.name
#再次调用wt.name,由于实例的name属性没有找到,类的name属性就显示出来了
print(wt.name) # Student
类属性作为计数器的例子:
# 为了统计学生人数,可以给Student类增加一个类属性,每创建一个实例,该属性自动增加:
class Student(object):
count = 0
def __init__(self, name):
self.name = name
Student.count = Student.count + 1
在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。
小结:
- 实例属性属于各个实例所有,互不干扰;
- 类属性属于类所有,所有实例共享一个属性;
- 不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误。