date: 2022-08-04
address: NC
author: 吴第广
参考资料:廖雪峰python教程
吴第广-【人工智能学习路线】-【人工智能开发入门】-【Python编程】
简介:编程语言。提供了非常完善的基础代码库,覆盖了网络、文件、GUI、数据库、文本等大量内容。除了内置的库外,还有大量的第三方库
许多大型网站都有python的身影,如YouTuBe,Instagram,还有国内的豆瓣。还有大公司在使用,其中包括Yahoo、NASA等
定位:优雅、明确、简单
适合哪些类型应用开发:网络应用:网站、后台服务,日常小工具:各类脚本
缺点:运行速度慢、不能加密
略
用于执行 .py 代码的环境
Python解释器很多,但使用最广泛的还是CPython
命令行模式和Python交互模式
命令模式:可以执行输入python
纪进入交互式环境,也可以执行python hello.py
运行一个.py
文件
>>> print("hello, world")
hello, world
>>> print('hello, world')
hello, world
# 文件名:calc.py
# 文件目录下cmd执行:python calc.py
# 打印:600
print(100 + 200 + 300)
下载并使用,替换自带的文本编辑器
windows 上是不行的,但是,在Mac和Linux上是可以的,方法就是在 .py 文件第一行加上一个特殊的注释:
#!/usr/bin/env python3
print('hello, world')
然后给 .py 文件执行权限,就可以直接运行 hello.py
了
chmod a+x hello.py
输出:
print('The quick brown fox', 'jumps over', 'the lazy dog')
输入:
>>> name = input('please enter your name: ')
please enter your name: wudiguang
>>> print('my name is:', name)
my name is: wudiguang
任何一种编程语言都有自己的一套语法,编译器或者解释器负责把服务语法的程序代码转换成CPU能够执行的机器码再执行
Python语法比较简单,采用缩进方式,如下格式:
# print absolute value of an integer:
a = int(input('input a number:'))
if a >= 0:
print(a)
else:
print(-a)
缩进有利有弊
利:强迫我们写出格式化的代码,强迫我们写出缩进较少的代码 弊:复制-粘贴失效
小结: Python采用缩进来组织代码块,请务必遵守约定俗成的习惯,坚持使用4个空格的缩进。另外,Python程序是大小写敏感的
\
,字符串不转移:r''
,多行保持格式:'''...'''
and
、or
和not
逻辑运算PI
在Python中,可以把任意数据类型数据赋值给同一个变量,这种变量类型不固定的语言称为动态语言
,与之对应的是静态语言
。静态语言在定义时必须指定变量类型,如Java
注意:Python的整数没有大小限制,浮点也没有大小限制,但是超出一定范围就直接表示为INF
字符编码:由于计算机只能识别0
1
组成的数据,需要将文本数据转成计算机能识别的数据格式才行,索引产生了字符编码来转换文本到计算机能直接的格式
多语言混合的文本会乱码,因此产生了Unicode字符集,把所有语言都统一到一套编码里,这样就不会有乱码问题了
ASCII:1个字节,最早只有127个字符被编码到计算机,即大小写英文字母、数字和一些符号 GB2312:中文编码 Shift_JIS:日文编码 Euc-kr:韩文编码 Unicode:通常使用两个字节来表示字符 UTF-8:可变长的编码
统一Unicode后又产生了新问题,用Unicode编码比ASCII编码多一倍的存储空间,在存储和传输上就十分不划算,于是产生了可变长的UTF-8
编码,UTF-8
编码把Unicode字符根据不同的数字大小编码成1-6个字节,通常使用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节
UTF-8
字符被转换为Unicode
字符到内存里,编辑完成后,保存的时候再把Unicode
转换为UTF-8
保存到文件Unicode
内容转换为UTF-8
再传输到浏览器在最新的Python 3版本中,字符串是以Unicode
编码,即Python字符串支持多语言
>>> print('包含中文的str')
包含中文的str
Python提供ord()
函数获取字符的整数表示
>>> ord('A')
65
>>> ord('中')
20013
>>> chr(66)
'B'
>>> chr(25991)
'文'
Python对bytes
类型的数据用带b
前缀的单引号或双引号表示:
x = b'ABC'
Unicode
表示的字符串通过encode()
方法可以编码为指定的bytes
,如:
>>> 'ABC'.encode('ascii')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
反过来,如果我们从网络或磁盘上读取字节流,那么读到的数据就是bytes
。要把bytes
变为字符串,就需要用decode()
方法
>>> b'ABC'.decode('ascii')
'ABC'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'
len('ABC')
计算字符串包含多少个字符
当我们源代码中包含中文时,需要指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按UTF-8编码读取,通常在文件开头添加下面两行注释:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
格式化输出
%
实现:>>> 'Hello, %s' % 'world'
'Hello, world'
>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
'Hi, Michael, you have $1000000.'
占位符:
%d:整数 %f:浮点数 %s:字符串(如果不确定数据类型,%s永远起作用,把任何数据类型转换为字符串) %x:十六进制整数
使用字符串的format()
方法,使用{0}、{1}…占位符
>>> 'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125)
'Hello, 小明, 成绩提升了 17.1%'
以f
开头的字符串,自动替换变量
>>> 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:不定长有序集合,里面元素数据类型可以不同,也可以嵌套list
>>> classmates = ['Michael', 'Bob', 'Tracy']
>>> classmates
['Michael', 'Bob', 'Tracy']
>>> L = []
>>> len(L)
0
tuple:有序list,一旦初始化就不能修改,不可变,所以代码更安全
>>> t = (1, 2)
>>> t
(1, 2)
>>> t = ()
>>> t
()
>>> t = (1,)
>>> t
(1,)
tuple不可变是指每个元素的指向对象不变,但是指向的对象内容可以变,即指针地址不变,地址中的内容可变
>>> t = ('a', 'b', ['A', 'B'])
>>> t[2][0] = 'X'
>>> t[2][1] = 'Y'
>>> t
('a', 'b', ['X', 'Y'])
例子:
age = 3
if age >= 18:
print('adult')
# `elif`是`else if`的简写
elif age >= 6:
print('teenager')
else:
print('kid')
if
判断条件还可以简写,如:
if x:
print('True')
只要 x
是非零数值、非空字符串、非空list等,就判断为True
,否则为False
for...in
names = ['Michael', 'Bob', 'Tracy']
for name in names:
print(name)
range()
函数生成一个整数序列,再通过list()
函数可以转换为list
>>> list(range(5))
[0, 1, 2, 3, 4]
sum = 0
n = 99
while n > 0:
sum = sum + n
n = n - 2
print(sum)
break
:提前结束循环
continue
:跳过当前这次循环,直接开始下一次循环
尽量避免使用break
和continue
,滥用会造成代码执行逻辑分叉过多,容易出错
Java
中的HashMap
,无序,key是不可变对象>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
>>> d['Michael']
95
使用dict['key']
时,如果key
值不存在,则会报错,避免报错的方法有两种:
in
判断key
是否存在
>>> 'Thomas' in d
False
get()
方法获取,如果key
不存在,则返回None
或者自己指定的value
>>> d.get('Thomas')
>>> d.get('Thomas', -1)
-1
pop('key')
:删除一个元素
>>> s = set([1, 2, 3])
>>> s
{1, 2, 3}
学习记录:2022-08-04 18:28:00
函数是最基本的一种代码抽象方式
官方函数网站:进入
可以使用 help(abs)
查看abs
函数的帮助信息
>>> abs(100)
100
>>> abs(-20)
20
>>> abs(12.34)
12.34
调用Python函数需要根据定义,传入正确的参数。如果函数调用出错,需要会看错误信息
# abstest.py
# -*- coding: utf-8 -*-
def my_abs(x):
if x >= 0:
return x
else:
return -x
把上述的my_abs()
函数定义保存到abstest.py
文件中,然后可以使用from abstest import my_abs
来导入my_abs()
函数,注意abstest
是文件名
空函数:空逻辑
def nop():
pass
参数检查:如只允许整数和浮点,当传入了不恰当的参数时,打印错误信息
# -*- coding: utf-8 -*-
def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -x
添加了参数检查后,如果传入错误的参数类型,函数就可以抛出一个错误:
Traceback (most recent call last):
File "client.py", line 2, in <module>
print(my_abs('A'))
File "abstest.py", line 4, in my_abs
raise TypeError('bad operand type')
TypeError: bad operand type
返回多个值:
import math
def move(x, y, step, angle=0):
nx = x + step * math.cos(angle)
ny = y - step * math.sin(angle)
return nx, ny
import math
语句表示导入math
包,并允许后续代码引用math
包里的sin
,cos
等函数
>>> x, y = move(100, 100, 60, math.pi / 6)
>>> print(x, y)
151.96152422706632 70.0
但其实这是一种假象,Python函数返回的仍然是单一值:
>>> r = move(100, 100, 60, math.pi / 6)
>>> print(r)
(151.96152422706632, 70.0)
原来返回值是一个tuple!但是,在语法上,返回一个tuple可以省略括号,而多个变量可以同事接收一个tuple,按位置赋给对应的值,所以Python函数返回值其实就是返回一个tuple。但写起来更方便
必选参数、默认参数、可变参数、关键字参数和命名关键字参数
默认参数值:
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s* x
return s
可变参数:
# 传入tuple或list
# 调用:calc(1, 2, 3)
def calc(numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
# 变长参数
# 调用:calc(1,2,3)
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
关键字参数:允许传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict
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'}
# 包含各类参数的函数定义
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}
def fact(n):
if n==1:
return 1
return n * fact(n - 1)
使用递归函数的优点是逻辑简单清晰,缺点是过深的调用会导致栈溢出。
针对尾递归优化的语言可以通过尾递归防止栈溢出。尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环。
Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。
代码越少,开发效率越高
取一个list或tuple的部分元素,如下list:
L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
for...in
可以使用list
、tuple
、dict
等
# list或tuple
for (i=0; i<length; i++) {
n = list[i];
}
d = {'a': 1, 'b': 2, 'c': 3}
# 默认迭代key
for key in d:
print(key)
# 迭代value
for value in d.values()
# 同时迭代key和value
for k, v in d.items()
判断一个对象是否可迭代:
from collections.abc import Iterable
isinstance('abc', Iterable)
# True
isinstance([1,2,3], Iterable)
# True
isinstance(123, Iterable)
# False
获取list每个元素下标:内置的enumerate
函数把list
变成索引-元素树
for i, value in enumerate(['A', 'B', 'C']):
print(i, value)
任何可迭代对象都可以作用于for
循环,包括我们自定义的数据类型,只要符合迭代条件,就可以使用for
循环
运用列表生成式,可以快速生成list,可以通过一个list推导出另一个list,而代码却十分简洁。
# [1*1, 2*2, 3*3..., 10*10]
[x * x for x in range(1, 11)]
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 筛选出仅偶数的平方
[x * x for x in range(1, 11) if x % 2 == 0]
# [4, 16, 36, 64, 100]
# 使用两层循环,生成全排列
[m + n for m in 'ABC' for n in 'XYZ']
一边循环一边计算的机制
# 定义generator
g = (x * x for x in range(10))
# 依次打印
next(g)
定义斐波拉契数列:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
上述的函数和generator很类似,要把fib
函数变成generator函数,只需要把print(b)
改为yield b
就可以了
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
generator函数执行逻辑:每次调用next()
时执行,遇到yield
语句返回,再次执行从上次返回的yield
语句处继续执行
generator是非常强大的工具,在Python中,可以简单地把列表生成式改成generator,也可以通过函数实现复杂逻辑的generator
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
函数式编程是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是正确的,输出就是确定的
特点:允许把函数本身作为参数传入另一个函数,还允许返回一个函数
变量可以指向函数名
f = abs
f(-10)
一个函数可以接收另一个函数作为参数–高阶函数
def add(x, y, f):
return f(x) + f(y)
x = -5
y = 6
f = abs
f(x) + f(y) ==> abs(-5) + abs(6) ==> 11
return 11
编写高阶函数,就是让函数的参数能够接收别的函数。
Iterator
,map
将传入的函数一次作用到序列的每个元素,并把结果作为新的Iterator
返回>>> def f(x):
... return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
map()
传入的第一个参数是f
,即函数对象本身。由于结果r
是一个Iterator
,Iterator
是惰性序列,因此通过list()
函数让它把整个序列都计算出来并返回一个list
>>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
['1', '2', '3', '4', '5', '6', '7', '8', '9']
[x1, x2, x3,...]
上,这个函数必须接收两个参数,reduce
把结果继续和序列的下一个元素做累积计算,其效果就是:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
用于过滤序列
和map()
类似,filter()
也接收一个函数和一个序列,不同的是,filter()
把传入的函数一次作用域每个元素,然后根据返回值是True
还是False
决定保留还是丢弃该值
例如,在一个list中删掉偶数,只保留奇数
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]
filter()
返回的是一个Iterator
,是一个惰性序列,所以要强迫filter()
完成计算结果,需要用list()
函数获得所有结果并返回list
小结:
filter()的作用是从一个序列中筛出符合条件的元素。由于filter()使用了惰性计算,所以只有在取filter()结果的时候,才会真正筛选并每次返回下一个筛出的元素。
排序
sorted([36, 5, -12, 9, -21])
# [-21, -12, 5, 9, 36]
# 按绝对值大小排序,key指定的函数将作用于list的每个元素上,并根据key函数返回的结果进行排序
sorted([36, 5, -12, 9, -21], key=abs)
# [5, 9, -12, -21, 36]
# 反向排序
sorted([36, 5, -12, 9, -21], key=abs, reverse=True)
函数作为返回值
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)
>>> f
<function lazy_sum.<locals>.sum at 0x101c6ed90>
>>> f()
25
闭包:
一个函数可以返回一个计算结果,也可以返回一个函数。
返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。
# 定义匿名函数,和下面函数定义功能相同
lambda x: x*x
def f(x):
return x*x
# 使用
list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
# 结果
[1, 4, 9, 16, 25, 36, 49, 64, 81]
# 匿名函数作为返回值返回
def build(x, y):
return lambda: x * x + y * y
Python对匿名函数的支持有限,只有一些简单的情况下可以使用匿名函数。
类似Java中的AOP(代理模式),可以在函数调用前后添加逻辑
def log(func):
def wrapper(*args, **kw):
# func.__name__ 可以获取到函数的名字
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
# 使用
@log
def now():
print('2015-3-25')
# 完整示例
import time, functools
def decorator(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
t = time.time()
fn()
print(f'cost:{time.time() - t:.4f}s')
return wrapper
@decorator
def fn():
print('Begin')
time.sleep(1)
print('End')
fn()
在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。
decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。
int('12345')
int('12345', base=8)
int('12345', base=2)
# 定义一个int2()函数来默认转为二进制
def int2(x, base=2):
return int(x, base)
# 使用偏函数
import functools
int2 = functools.partial(int, base=2)
int2('1000000')
一个.py文件就称之为一个模块(Module)
好处:
为了避免模块名冲突,引入按目录来组织模块的方法,称为包(Package)
总结 模块是一组Python代码的集合,可以使用其他模块,也可以被其他模块使用。
创建自己的模块时,要注意:
Python内置了很多有用的模块,可以直接被使用,如下使用sys
模块
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' a test module '
__author__ = 'wudiguang'
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()
运行结果如下:
$ python3 hello.py
Hello, world!
$ python hello.py Michael
Hello, Michael!
作用域:
外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。
在Python中,安装第三方模块是通过包管理工具pip
完成的
一般来说,第三方库都会在Python官方的https://pypi.python.org
网站注册,可以在官网查看目标库,如安装Pillow
pip install Pillow
使用Anaconda:
默认安装数十个第三方模块
OOP,同Java
数据封装、继承和多态是面向对象的三大特点
# 继承 object
class Student(object):
# 实例创建会调用__init__方法,第一个参数是对象本身
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))