Python 学习笔记 - 钢钢更新

Python3 学习笔记 - 钢钢更新

@(我的笔记本)[Python]

[toc]

List

定义list

1
list=['a','b','c']

获取list长度

1
>>> len(list)

3

获取list指定位置的值

1
list[0]

‘a’

1
list[2]

‘c’

1
list[3]

Traceback (most recent call last):
File ““, line 1, in
IndexError: list index out of range

1
list[-1]

‘c’

往list中插入新值(默认插入到最后)

1
2
list.append('d')
list

[‘a’, ‘b’, ‘c’, ‘d’]

往list中特定位置插入值

1
2
list.insert(1,'e')
list

[‘a’, ‘e’, ‘b’, ‘c’, ‘d’]

从list中删除值(默认从最后删除)

1
2
list.pop()
list

‘d’
[‘a’, ‘e’, ‘b’, ‘c’]

往list中特定位置删除值

1
2
list.pop(2)
list

‘b’
[‘a’, ‘e’, ‘c’]

覆盖list中的特定位置的值

1
2
list[1]='f'
list

[‘a’, ‘f’, ‘c’]

Tuple

定义tuple

1
2
l=(1)
l

1

1
2
l=(1,)
l

(1,)

tuple里嵌套list

1
2
l=('a','b',['A','B'],'c')
len(l)

4

可以把tuple当做多维数组使用

1
l[2][1]

‘B’

1
l

(‘a’, ‘b’, [‘A’, ‘B’], ‘c’)

1
2
l[2][1]='X'
l

(‘a’, ‘b’, [‘A’, ‘X’], ‘c’)

if 条件判断(别忘了写冒号)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
height=1.75
weight=80.5
bmi = weight/height/height

if bmi <= 18.5:
print(bmi,"过轻”)
elif 18.5<bmi and bmi<=25:
print(bmi,"正常")
elif 25<bmi and bmi<=28:
print(bmi,"过重")
elif 28<bmi and bmi<=32:
print(bmi,"肥胖")
else:
print(bmi,"严重肥胖")

分隔字符串

1
2
3
words = text.split()
for word in words:
print(word)

循环

for循环

1
2
3
l=['a','b','c']
for l1 in l:
print(l1)

range用法

1
2
range(101)
range(0, 101)
1
2
for i in range(5,0,-1):
print(i)

5
4
3
2
1

while循环(break提前退出)

1
2
3
4
5
6
n=1
while n<=100:
if n>20:
break
print(n)
n=n+1

while循环(continue跳过此次循环)

1
2
3
4
5
6
n=0
while n<10:
n=n+1
if n%2==0:
continue
print(n)

Dict

key-value mapping表:空间换时间

1
2
d={'Kenny': 34, 'Bob': 40, 'Steven': 39}
d['Kenny']

34

1
2
d['Steven']=40
d

{‘Steven’: 40, ‘Kenny’: 34, ‘Bob’: 40}

1
'kenny' in d

False

1
'Kenny' in d

True

获取dict元素值

1
d.get('Kenny')

39

删除dict元素值

1
2
d.pop('Steven')
d

40
{‘Kenny’: 34, ‘Bob’: 40}

更新 (插入) dict元素值

1
2
d['Tao']=33
d

{‘Kenny’: 34, ‘Bob’: 40, ’Tao’: 33}

Set集合(无序,不重复)

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

{1, 2, 3, 4}

新增set值

1
2
s.add(5)
s

{1, 2, 3, 4, 5}

删除set值

1
2
s.remove(3)
s

{1, 2, 4, 5}

函数

1
2
3
4
5
6
7
8
9
def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
if x>=0:
return x
else:
return -x

my_abs(-10)

10

函数返回多个值

1
2
3
4
5
6
7
8
9
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

在函数内修改全局变量

1
2
3
4
5
6
7
8
count = 5
def myfun():
global count
count = 13
print(count)


myfun()

13

1
count

13

lambda匿名函数

1
2
3
4
def ds(x):
return 2*x+1

ds(5)

11

1
2
lambda x:2*x+1
s(5)

11

1
2
g = lambda x, y : x + y
g(3,4)

7

定义默认参数要牢记一点:默认参数必须指向不变对象!
要修改上面的例子,我们可以用None这个不变对象来实现:

1
2
3
4
5
6
7
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L

add_end()

[‘END’]

我们以数学题为例子,给定一组数字a,b,c……,请计算a2 + b2 + c2 + ……。
要定义出这个函数,我们必须确定输入的参数。由于参数个数不确定,我们首先想到可以把a,b,c……作为一个list或tuple传进来,这样,函数可以定义如下:

1
2
3
4
5
def calc(numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum

但是调用的时候,需要先组装出一个list或tuple:

1
calc([1, 2, 3])

14

1
calc((1, 3, 5, 7))

84

如果利用可变参数,调用函数的方式可以简化成这样:

1
calc(1, 2, 3)

14

1
calc(1, 3, 5, 7)

84

所以,我们把函数的参数改为可变参数:

1
2
3
4
5
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum

定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号。在函数内部,参数numbers接收到的是一个tuple。因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数:

1
calc(1, 2)

5

1
calc()

0

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

1
2
nums = [1, 2, 3]
calc(nums[0], nums[1], nums[2])

14

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

1
2
nums = [1, 2, 3]
calc(*nums)

14

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

小结

  • Python的函数具有非常灵活的参数形态,既可以实现简单的调用,又可以传入非常复杂的参数。
  • 默认参数一定要用不可变对象,如果是可变对象,程序运行时会有逻辑错误!
  • 要注意定义可变参数和关键字参数的语法;
  • *args是可变参数,args接收的是一个tuple;
  • 可变参数既可以直接传入:func(1, 2, 3),又可以先组装list或tuple,再通过args传入:func((1, 2, 3));
  • **kw是关键字参数,kw接收的是一个dict。
  • 关键字参数既可以直接传入:func(a=1, b=2),又可以先组装dict,再通过kw传入:func({‘a’: 1, ‘b’: 2})。
  • 使用*args和**kw是Python的习惯写法,当然也可以用其他参数名,但最好使用习惯用法。
  • 命名的关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。
  • 定义命名的关键字参数在没有可变参数的情况下不要忘了写分隔符*,否则定义的将是位置参数。

递归函数

1
2
3
4
5
6
def fact(n):
if n==1:
return 1
return n* fact(n-1)

fact(1)

1

1
fact(5)

120

上面的fact(n)函数由于return n * fact(n - 1)引入了乘法表达式,所以就不是尾递归了。要改成尾递归方式,需要多一点代码,主要是要把每一步的乘积传入到递归函数中:

1
2
3
4
5
6
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)

小结

  • 使用递归函数的优点是逻辑简单清晰,缺点是过深的调用会导致栈溢出。
  • 针对尾递归优化的语言可以通过尾递归防止栈溢出。尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环。
  • Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。

用循环代替上面的递归

1
2
3
4
5
6
def fact(n):
result = 1
while n > 1:
result = result * n
n = n - 1
return result

list切片功能(数字代表的是索引位置)

1
2
L=['kenny','bob','steven','tao','lawrence']
L[0:2]

[‘kenny’, ‘bob’]

1
L[:2]

[‘kenny’, ‘bob’]

1
L[2:2]

[]

1
L[2:4]

[‘steven’, ‘tao’]

1
L[-1:]

[‘lawrence’]

1
L[-1:-2]

[]

1
L[-2:-1]

[‘tao’]

1
L=list(range(100))

可以通过切片轻松取出某一段数列,比如:

前10个数

1
L[:10]

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

后10个数

1
L[-10:]

[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

前11-20个数

1
L[10:20]

[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

前10个数,每两个取一个

1
L[:10:2]

[0, 2, 4, 6, 8]

所有数,每5个取一个

1
L[::5]

[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]

tuple也是一种list,唯一区别是tuple不可变。因此,tuple也可以用切片操作,只是操作的结果仍是tuple:

1
(0, 1, 2, 3, 4, 5)[:3]

(0, 1, 2)

字符串’xxx’也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串:

1
'ABCDEFG'[:3]

‘ABC’

1
'ABCDEFG'[::2]

‘ACEG’

在很多编程语言中,针对字符串提供了很多各种截取函数(例如,substring),其实目的就是对字符串切片。Python没有针对字符串的截取函数,只需要切片一个操作就可以完成,非常简单。

迭代(for循环)

1
2
3
d = {'a': 1, 'b': 2, 'c': 3}
for key in d:
print(key)

a
c
b

1
2
for value in d.values():
print(value)

1
3
2

1
2
for k,v in d.items():
print(k,v)

(‘a’, 1)
(‘c’, 3)
(‘b’, 2)

列表生成式

1
list(range(1, 11))

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

但如果要生成[1x1, 2x2, 3x3, …, 10x10]怎么做?方法一是循环:

1
2
3
4
5
L = []
for x in range(1, 11):
L.append(x * x)

L

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

但是循环太繁琐,而列表生成式则可以用一行语句代替循环生成上面的list:

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

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:

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

[4, 16, 36, 64, 100]

还可以使用两层循环,可以生成全排列:

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

[‘AX’, ‘AY’, ‘AZ’, ‘BX’, ‘BY’, ‘BZ’, ‘CX’, ‘CY’, ‘CZ’]

把一个list中所有的字符串变成小写:

1
2
L = ['Hello', 'World', 'IBM', 'Apple']
[s.lower() for s in L]

[‘hello’, ‘world’, ‘ibm’, ‘apple’]

生成器generator

1
2
3
g=(x*x for x in range(10))
for n in g:
print (n)

0
1
4
9
16
25
36
49
64
81

也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

1
2
3
4
5
6
7
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'

map/reduce

map用法

map()传入的第一个参数是f,即函数对象本身。由于结果r是一个Iterator,Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list。

1
2
3
4
5
6
def f(x):
return x*x


r=map(f,[1,2,3,4,5,6,7,8,9])
r

[1, 4, 9, 16, 25, 36, 49, 64, 81]

reduce的用法

reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算

1
2
3
4
5
from functools import reduce
def add(x,y):
return x+y

reduce(add, [1,3,5,7,9])

25

体会filter和map的区别,filter是过滤结果为真的数据,而map则是迭代调用

1
list(filter(lambda x:x*2,range(10)))

[1, 2, 3, 4, 5, 6, 7, 8, 9]

1
list(map(lambda x:x*2,range(10)))

[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

filter过滤

Python内建的filter()函数用于过滤序列。
和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

1
2
3
4
def is_odd(n):
return n%2==1

list(filter(is_odd, [1,2,4,5,6,9,10,15]))

[1, 5, 9, 15]

sorted排序

Python内置的sorted()函数就可以对list进行排序

1
sorted([36,5,-12,9,-21])

[-21, -12, 5, 9, 36]

1
sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)

[‘Zoo’, ‘Credit’, ‘bob’, ‘about’]

函数作为返回值

1
2
3
4
5
6
7
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum

当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:

1
2
f = lazy_sum(1, 3, 5, 7, 9)
f

<function lazy_sum..sum at 0x101c6ed90>

调用函数f时,才真正计算求和的结果:

1
f()

25

装饰器decorator

由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。

1
2
3
4
5
def now():
print('2015-3-25')

f = now
f()

2015-3-25

函数对象有一个name属性,可以拿到函数的名字:

1
now.__name__

‘now’

1
f.__name__

‘now’

现在,假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:

1
2
3
4
5
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper

观察上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:

1
2
3
@log
def now():
print('2015-3-25')

调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:

1
now()

call now():
2015-3-25

functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。

functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2:

1
2
3
import functools
int2 = functools.partial(int, base=2)
int2('1000000')

64

1
int2('1010101')

85

使用模块

Python本身就内置了很多非常有用的模块,只要安装完毕,这些模块就可以立刻使用。
我们以内建的sys模块为例,编写一个hello的模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' a test module '
__author__ = 'Michael Liao'
import sys
def test():
args = sys.argv
if len(args)==1:
print('Hello, world!')
elif len(args)==2:
print('Hello, %s!' % args[1])
else:
print('Too many arguments!')
if __name__=='__main__':
test()
  • 第1行和第2行是标准注释,第1行注释可以让这个hello.py文件直接在Unix/Linux/Mac上运行,第2行注释表示.py文件本身使用标准UTF-8编码;
  • 第4行是一个字符串,表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释;
  • 第6行使用author变量把作者写进去,这样当你公开源代码后别人就可以瞻仰你的大名;

以上就是Python模块的标准文件模板,当然也可以全部删掉不写,但是,按标准办事肯定没错。

后面开始就是真正的代码部分。

你可能注意到了,使用sys模块的第一步,就是导入该模块:

1
import sys

导入sys模块后,我们就有了变量sys指向该模块,利用sys这个变量,就可以访问sys模块的所有功能。
sys模块有一个argv变量,用list存储了命令行的所有参数。argv至少有一个元素,因为第一个参数永远是该.py文件的名称,例如:
运行python3 hello.py获得的sys.argv就是['hello.py']
运行python3 hello.py Michael获得的sys.argv就是['hello.py', 'Michael]

最后,注意到这两行代码:

1
2
if __name__=='__main__':
test()

当我们在命令行运行hello模块文件时,Python解释器把一个特殊变量__name__置为__main__,而如果在其他地方导入该hello模块时,if判断将失败,因此,这种if测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试。
我们可以用命令行运行hello.py看看效果:

1
$ python3 hello.py

Hello, world!

1
$ python hello.py Michael

Hello, Michael!

如果启动Python交互环境,再导入hello模块:

1
$ python3

Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 23 2015, 02:52:03)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type “help”, “copyright”, “credits” or “license” for more information.

1
import hello

导入时,没有打印Hello, word!,因为没有执行test()函数。
调用hello.test()时,才能打印出Hello, word!:

1
hello.test()

Hello, world!

try … except 捕获错误

使用try…except捕获错误还有一个巨大的好处,就是可以跨越多层调用,比如函数main()调用foo(),foo()调用bar(),结果bar()出错了,这时,只要main()捕获到了,就可以处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e:
print('Error:', e)
finally:
print('finally...')
try:
sum = 1 + 'a'
f = open('xyz.txt')
print(f.read())
f.close()
except OSError as reason:
print("文件打开出错。\n", str(reason))
except TypeError as reason:
print("类型出错。", str(reason))
finally:
f.close()

抛出错误

因为错误是class,捕获一个错误就是捕获到该class的一个实例。因此,错误并不是凭空产生的,而是有意创建并抛出的。Python的内置函数会抛出很多类型的错误,我们自己编写的函数也可以抛出错误。

如果要抛出错误,首先根据需要,可以定义一个错误的class,选择好继承关系,然后,用raise语句抛出一个错误的实例:

1
2
3
4
5
6
7
8
9
# err_raise.py
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return 10 / n
foo('0')

日期和时间

获取当前日期和时间:

1
2
3
from datetime import datetime
now = datetime.now() # 获取当前datetime
print(now)

2015-05-18 16:28:07.198690

1
print(type(now))

获取指定日期和时间

要指定某个日期和时间,我们直接用参数构造一个datetime:

1
2
3
from datetime import datetime
dt = datetime(2015, 4, 19, 12, 20) # 用指定日期时间创建datetime
print(dt)

2015-04-19 12:20:00

datetime转换为timestamp

把一个datetime类型转换为timestamp只需要简单调用timestamp()方法:

1
2
3
from datetime import datetime
dt = datetime(2015, 4, 19, 12, 20) # 用指定日期时间创建datetime
dt.timestamp() # 把datetime转换为timestamp

1429417200.0

timestamp转换为datetime

要把timestamp转换为datetime,使用datetime提供的fromtimestamp()方法:

1
2
3
from datetime import datetime
t = 1429417200.0
print(datetime.fromtimestamp(t))

2015-04-19 12:20:00

str转换为datetime

很多时候,用户输入的日期和时间是字符串,要处理日期和时间,首先必须把str转换为datetime。转换方法是通过datetime.strptime()实现,需要一个日期和时间的格式化字符串:

1
2
3
from datetime import datetime
cday = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S')
print(cday)

2015-06-01 18:19:59

datetime转换为str

如果已经有了datetime对象,要把它格式化为字符串显示给用户,就需要转换为str,转换方法是通过strftime()实现的,同样需要一个日期和时间的格式化字符串:

1
2
3
from datetime import datetime
now = datetime.now()
print(now.strftime('%a, %b %d %H:%M'))

Mon, May 05 16:28

base64

Python内置的base64可以直接进行base64的编解码:

1
2
import base64
base64.b64encode(b'binary\x00string')

b’YmluYXJ5AHN0cmluZw==’

1
base64.b64decode(b'YmluYXJ5AHN0cmluZw==')

b’binary\x00string’

由于标准的Base64编码后可能出现字符+和/,在URL中就不能直接作为参数,所以又有一种”url safe”的base64编码,其实就是把字符+和/分别变成-和_:

1
base64.b64encode(b'i\xb7\x1d\xfb\xef\xff')

b’abcd++//‘

1
base64.urlsafe_b64encode(b'i\xb7\x1d\xfb\xef\xff')

b’abcd–__

1
base64.urlsafe_b64decode('abcd--__')

b’i\xb7\x1d\xfb\xef\xff’

文件

打开文件

1
f = open('/u01/1.txt')

重置游标

1
2
f.seek(0,0)
f.read(100)

打印文件内容

1
2
for line in f:
print(line)

数据库

连接MySQL

1
2
3
4
5
6
7
8
9
10
11
12
import pymysql
user = 'root'
pwd = 'oracle'
host = '127.0.0.1'
db = 'v4crm'

cnx = pymysql.Connect(user=user, password=pwd, host=host, database=db)
cursor = cnx.cursor()
print(cursor)
print(cnx)
cursor.close()
cnx.close()

本文标题:Python 学习笔记 - 钢钢更新

文章作者:王方钢 / Kenny Wang

发布时间:2019年07月11日 - 13:07

最后更新:2019年07月11日 - 13:07

原始链接:https://wangfanggang.com/Python/python/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

王方钢 / Kenny Wang wechat
0%