Python 廖雪峰: https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000
Python 的安装及.........配置(Mac 系统下)
Mac 系统下的安装
-
安装 Homebrew
~ 理解 homebrew 和 npm 区别
homebrew: osx的软件管理工具, 软件管理助手, 可以安装Chrome浏览器等可视化工具
npm: node.js的程序/模块管理工具, 服务于JavaScript社区
~ 安装 homebrew:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
-
安装 Python
~ python2.x 和 python3.x
python2.x: Mac 系统自带 Python2.7(老, 被取代)
python3.x: ① 官网下载安装 ② 通过 brew
brew install python3
安装 -
Python 解释器(交互式命令行)
CPython(下载安装 python 即完成): 解释器是用 C 语言开发的,所以叫 CPython。在命令行下运行 python 就是启动 CPython 解释器
使用
终端中运行
python
, 启动 python2.x终端中运行
python3
, 启动 Python3.xCPython 使用
>>>
, 作为提示符, 使用exit()
作为退出 -
文本编辑器 (VScode)
安装拓展
Python
vscode 报错:
Linter pylint is not installed
(代码规范的插件)
~ 参考:
https://blog.csdn.net/yh1061632045/article/details/81665446
~ 手动切换 python2.7 --> python3.7(地址是:
/usr/local/bin/python3
)~ 配置 vscode, 设置
python.pythonPath: /usr/local/bin/python3
~ 然后点击
install pylint
, 安装完成 vscode 就不弹出提示, 同时能代码提示
win 系统下的安装
官网下安装
勾选
Add Python 3.7 to PATH
, 然后安装即可在命令提示符中,
python
会出来>>>
即代表安装成功可能报错
code2305
第一个 Python 程序
-
步骤
添加
.py
文件, 编写print('hello world!')
-
执行.py 文件
利用 python 命令: cd 到文件所在目录 -> 终端执行
python demo.py
-> 打印出结果(win, mac)直接执行.py 文件: cd 到文件目录 -> 终端中
chmod a+x demo.py
给与文件可执行的能力 -> 终端中./demo.py
-> 打印结果(mac系统)直接执行.py文件: 终端中
demo.py
-> 即会执行(win系统)
-
交互模式 和 直接执行.py 文件的区别
交互模式: 启动 Python 解释器, 输入一行解释一行, 不做保存(适合单端代码的测试)
.py 文件: 一次性执行完毕, 不给打断
-
print()输出
逗号以空格输出:
print('hello', 'world') -> 逗号以空格输出 -> hello world
自动计算:
print(300 + 300) -> 600(可以做计算)
print('300 + 100 =', 300 + 100) -> 300 + 100 = 600
-
input 和 变量
input 默认输出 str, 所以如果需要转化成整数需要用到
int(input())
当
int('abc')
, 发现 abc 不能转化成整数的时候, 就会报ValueError
的错name = input()
回车 ->>>>
等待输入值 -> name 存储你输入的值 ->>>>name
-> 打印输入值
Python 代码运行助手
作用: 让你在线输入 Python 代码,然后通过本机运行的一个
Python脚本(就是learning.py)
来执行代码, 直接在网页输出结果终端执行
python learning.py
-> 终端输出Ready for Python code on port 39093...
等代表运行成功打开
https://local.liaoxuefeng.com:39093/
, 就是 Python 的在线编辑器, 点击 run 即可输出结果网页 run 不输出结果的话,
Ctrl + c
中断, 再执行步骤 2
基本语法
基础
-
基础
-
#
: 注释 -
:
: 当语句以冒号:结尾时,缩进的语句视为代码块(相当于js的大括号) - Python 大小写敏感
-
-
数据类型和变量
- 整数: 十进制, 二进制, 十六进制
- 浮点数: 小数
- 字符串:
'
,"
- 转义符:
'I\'m \"OK\"!'
=>I'm "OK"!
,\n
表示换行,\t
表示制表符,字符\本身也要转义,所以\\
表示的字符就是\ - 不转义
r''
,''
内部字符串不转义, 直接输出 - 换行
'''...'''
, 多行时, 比\n
来的方便# 交互命令行内... 是提示符, 并不需要手动输入 print('''line1 ... line2 ... lin3''') # .py文件中 print('''line1 line2 line3''') # 还可以 print(r'''line1 # line2 # line3''')
- 布尔值(True, False) 和 布尔运算
# 布尔值, 注意大小写 >>> False False # 布尔运算 >>> 3 > 2 True # and、or和not运算 >>> True and False False >>> True or False True >>> not 1 > 2 True
- 空值:
None
- 变量: 变量名必须是大小写英文、数字和_的组合,且不能用数字开头
- 常量: 通常用全部的大写
- 除法:
/
, 得到的都是浮点数,>>> 9/3
->3.0
- 地板除:
//
, 只取结果的整数部分 - 取余数:
%
总结: 万物皆对象, Python 对整数无大小限制,对浮点数超出一定范围直接显示
inf
(无限大)
字符串编码
ord()
, 函数获取字符的整数表示, 英文转 ascii 码的数字chr()
, 函数把编码转换为对应的字符, 相反b'ABC'
, 把 Python 的 str 转成以字节为单位的 bytes,ABC
和b'abc'
, 一个是 str, 一个是 bytes, 每个字符只占一个字节-
str
通过encode()
方法可以编码为指定的 bytes>>> 'ABC'.encode('ascii') b'ABC' >>> '中文'.encode('utf-8') b'\xe4\xb8\xad\xe6\x96\x87' # 中文可以转成utf-8, 但是不可以转成ascii, 因为中文超出范围 # 在bytes中,无法显示为ASCII字符的字节,用\x##显示。 >>> '中文'.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)
-
bytes
通过decode
方法, 转成 str>>> 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') '中'
-
len()
函数计算的是 str 的字符数,如果换成 bytes,len()函数就计算字节数>>> len(b'ABC') 3 >>> len(b'\xe4\xb8\xad\xe6\x96\x87') 6 >>> len('中文'.encode('utf-8')) 6
-
str 和 bytes 的互相转换。为了避免乱码问题,应当使用 UTF-8 编码对 str 和 bytes 进行转换
# 为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释; # 告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。 #!/usr/bin/env python3 # -*- coding: utf-8 -*-
-
格式化(有点像 js 的模板字符串)
# %s:字符串, %d:整数, %f: 浮点数, %x:十六进制整数 # 需要替换的地方依次填写参数 # %% 代表转义 % >>> 'hello, %s, you have $%d' % ('bin.wang', 100) 'hello, bin.wang, you have $100'
计算机储存使用 Unicode,只有在传输的时候才转换成 UTF-8
-
str 不可变性
>>> a = 'abc' # replace方法 >>> a.replace('a', 'A') # 变的是变量a, 字符串'abc'并没有变 >>> a 'Abc'
list 和 tuple
-
list(类比 js 数组)
>>> list = ['one', 'two', 'three'] # 求得list长度 >>> len(list) 3 # list下标代表具体某个值 >>> list[len(list) - 1] 'three' # -1 代表最后一个, -2代表倒数第二个 >>> list[-1] # 正向反向越界都报 `IndexError` 的错
-
list 的方法
append()
: list.appen('test'), 末尾追加pop()
: list.pop(), 删除末尾, list.pop(1), 删除下标为 1 的那个insert
: list.insert(1, 'test'), 在索引 1 的地方插入sort()
: list.sort(), 根据 ascii 大小排序
-
tuple(元组)
-
跟 list 类似, 但是 tuple 一旦初始化就不能修改, 取得方式一致, 但是没有 list 的方法
>>> tuple = (1, 2, 3) >>> tuple[0] 1 # 注意当python只有一个元素的元组时候, 必须带上逗号, 以免识别成数学的括号运算 >>> t = (1, )
-
关于 tuple 的不可变
# 元组指向list是一直不变的, 只是list可变 >>> t = ('a', 'b', ['c', 'd']) >>> t[2][0] = 'X' >>> t[2][1] = 'Y' >>> t ('a', 'b', ['X', 'Y'])
-
条件判断
-
条件判断
age = 20 # 每个条件判断以 : 分割, elif 是 else if的缩写 if age >= 18: print('成年人') elif age >= 16: print('青少年') else: print('少年')
循环
-
for...in
循环(list, tuple, dict都可使用)name = ['Bob', 'Jack'] # Python 以 : 代替JS的 {} for key in name: print(key)
range()
函数,可以生成一个整数序列-
list()
函数, 可以转换为 list# range 生成从0开始小于5的整数, list把他们合成数组 >>> list(range(5)) [0, 1, 2, 3, 4]
-
while
循环, 只要条件满足,就不断循环num = 5 sum = 0 while num > 0: sum = sum + num num = num - 1 print(sum)
break
, 可提前结束循环continue
语句跳过某些循环
dict 和 set
-
dict, 官网说像 map, 个人觉得像对象
>>> d = {'Nacy': 83, 'Bob': 93} >>> d['Bob'] 93 # 多次赋值, 会覆盖 >>> d['Bob'] = 98 >>> d['Bob'] 98
-
dict 的 key 值
key 值不存在, 会报错
KeyError
-
判断 key 值存在
b = {'Bob': 123} # 第一种in >>> 'test' in b False # 第二种 get()方法,返回None 在交互模式下None不显示 >>> b.get('test') # 不显示 >>> b.get('Bob') 123 >>> b.get('test', -1) -1 >>> b.get('Bob', -1) 123
-
dict 和 list 的区别
- dict: key 找 value, 根据 key 值算出(哈希算法)'页码', 再根据页码查找 value, 所以占用内存较多
- list: 下标找 value, 查找是从头到尾, 直到找到, list 越长, 耗时慢
-
删除一个 key,
pop()
方法, 对应的 value 也会被删除相反, 新增的话直接是
d['Bob'] = '123'
, 即可
-
set
-
传入 list 作为输入集合, 重复元素自动被过滤, 只存储 key, 不存 value, 且无序
>>> s = set([1, 1, 2, 2, 3, 3]) >>> s {1, 2, 3} # 通过add(key)添加新key, 重复添加没效果 >>> s.add(4) >>> s {1, 2, 3, 4} # 通过remove(key)方法可以删除元素 >>> s.remove(1) >>> s {2, 3, 4}
-
set 可以看成无序和无重复元素的集合, 因此,两个 set 可以做数学意义上的交集、并集等操作
>>> s1 = set([1, 2, 3]) >>> s2 = set([2, 3, 4]) >>> s1 & s2 {2, 3} >>> s1 | s2 {1, 2, 3, 4}
-
函数
内置函数
参考地址:
https://docs.python.org/3/library/functions.html
求绝对值函数: abs(num)
abs(-100) -> 100
求最大值: max(num1, num2)
max(1, 2, -3) -> 2
-
类型转换, 交互模式下, help(hex)查看 hex 函数作用
>>> int('123') 123 >>> int(12.84) 12 >>> int('abc') # int第二个参数base是代表进制, 第一个参数是16进制, 转成十进制, 16^1 + 6 = 22 >>> int('16', base=16) 22 >>> int('123', 8) 83 # 报错 ValueError >>> float('12.34') 12.34 >>> str(1.23) '1.23' >>> str(100) '100' >>> bool(1) True >>> bool('') False
自定义函数
-
def
定义函数# 在交互模式下直接敲出函数 # 没有定义return的, 最后还是会return None def my_abs(x): if x >= 0: return x else: return -x # 在交互模式下 import 引入函数 >>> from demo(文件名不带.py) import my_abs(函数名) >>> my_abs(-10) 10
-
定义空函数
def nop(): # pass相当于占位符,代表不干啥事, 不填写会报错 pass
-
检查参数
def my_abs(x): # isinstance判断参数类型是不是整数或者浮点数, 不是就raise 一个错误 if not isinstance(x, (int, float)): raise TypeError('参数错误') if x >= 0: return x else: return -x
-
返回多个值
>>> def Fn(x, y): return x, y >>> r = Fn('abc', 'cba') >>> print(r) # 返回多个值其实返回的是一个tuple, 返回一个tuple可以省略括号 ('abc', 'cba')
函数的参数(挺绕的, 多练习)
-
位置参数(必选参数)
# x必须且依次, 即位置参数 def sum (x): return x * x
-
默认参数
概念: 默认参数必须要用不可变对象
#必选参数在前, 默认参数在后 def sum (x, n = 2, age = 18) pass # n还是使用默认值 sum(2, age = 16) # 默认参数的坑, 默认参数必须指向不变对象! def add_end(L = []): L.append('END') return L # 调用三次, 会一直加, 原因在: Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。 add_end() ['END', 'END', 'END'] # 修改 def add_end(L = None): if L is None: L = [] L.append('END') return L
-
可变参数
概念: 允许传入0个或若干个参数, 定义时参数带
*
# 定义一个a^2 + b^2...的函数 def getSum(*number): sum = 0 for key in number: sum = sum + key * key return sum tuple = (3, 4) # 直接传参, 调用时自动组装成tuple getsum(1, 2) # 直接拿tuple使用. 记得带星号 getSum(*tuple)
-
关键字参数
概念: 允许传入0个或若干个含参数名的参数, 调用时自动组成dict, 定义和调用都需要使用
**
def person(name, age, **kw): print('name: %s, age: %d, other: %s' % (name, age, kw)) dict = {'city': 'sz'} # kw得到的是dict的拷贝 person('bin.wang', 18, **dict)
-
命名关键字参数
概念: 为限制关键字参数名字, key = value, 且不是dict
# 以*为分割, 后面的为命名关键字参数 def person(name, *, city = 'SZ', age) print(name, city, age) person('test', age = 18) # 传参时, 必须传入参数名和参数值, 否则相当于多传了位置参数, 报错 person('test', 18) # 命名关键字参数可以由默认值, city
-
参数组合
概念: 参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数
def f1(a, b, c=0, *args, **kw): >>> f1(1, 2, 3, 'a', 'b') a = 1 b = 2 c = 3 args = ('a', 'b') kw = {} # 通过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': '#'} # 必选, 默认, 命名关键字参数, 关键字参数 def f2(a, b, c=0, *, d, **kw): >>> f2(1, 2, d=99, ext=None) a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}
递归函数
函数内部, 调用本身, 过深的调用可能导致栈溢出
栈溢出科普: 计算机中函数的调用通过栈数据结构实现的, 每进一个函数, 栈就加一层栈帧, return一次就减少一层栈帧, 当函数调用过多, 即会栈溢出
-
python栈溢出报错:
RecursionError: maximum recursion depth exceeded in comparison
def getFn(n): if n == 1: return 1 return n * getFn(n - 1)
例子: 移动汉诺塔
高级特性
切片(slice)
-
对list, tuple, str的数据处理
# 对list或者tuple L = [] n = 0 while n <= 99: L.append(n) n = n + 1 # 生成L是[0-99]的list # slice切片, 取索引0到索引3(不包括3), 即0, 1, 2 >>> L[ : 3] >>> [0, 1, 2] # L[-1]代表最后一位, 那么包括倒数第三位, 不包括倒数第一位 >>> L[-3: -1] >>> [97, 98] # 前十位, 每隔5位取一个 >>> L[ : 10 : 5] >>> [0, 5] # 所有list, 隔20取一位 >>> L[ : : 20] >>> [0, 20, 40, 60, 80] # 完全复制一个list >>> n = L[ : ]
>>> str = 'mytest' # 每隔2位取一位 >>> str[ : : 2] >>> 'mts'
迭代
for...in...
遍历也叫迭代, list, tuple, dict, str, generator都可迭代-
迭代的用法
-
list 和 tuple的迭代
# 如果迭代的时候想把下标输出 # 引用python内置的enumerate函数 >>> list = [4, 5, 6] >>> for index, value in enmurate(list): >>> 0 4 1 5 2 6 # 常见多变量 >>> for x, y in [(1, 1), (2, 4)]: ... print(x, y) >>> 1 1 2 4
-
dict的迭代
>>> dict = {'Bob': 18, 'Nancy': 17} # 获取key值 >>> for key in dict: >>> 'Bob' 'Nancy' # 获取value >>> for value in dict.values(): # 获取key和value值 >>> for key, value in dict.items(): >>> Bob 18 Nancy 17
-
str的迭代
>>> str = 'test' >>> for key in str: >>> t e s t
-
-
检测可迭代对象(区别于Iterator, 迭代器)
- 概念: 只要是可迭代对象, for循环就不会报错, 通过collections模块的Iterable类型判断可迭代对象
>>> from collections import Iterable >>> isinstance([1,2,3], Iterable) >>> True >>> isinstance('str', Iterable) >>> True >>> isinstance(123, Iterable) >>> False
列表生成式
- 概念: Python内置的用来创建list的生成式
# list部分 >>> list(range(1, 6)) >>> [1, 2, 3, 4, 5] # 生成 [1 * 1, 2 * 2, 3 * 3] >>> [x * x for x in range(1, 4)] >>> [1, 4, 9] # 还可以进行筛选 >>> [x * x for x in range(1, 4) if x % 2 == 0] >>> [4] # 使用两层循环,可以生成全排列, 即排列组合 >>> [m + n for m in 'ABC' for n in 'XYZ'] ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ'] # 把list中所有的字符串变成小写 >>> list = ['Hello', 'World'] >>> [s.lower() for s in list] >>> ['hello', 'world] # dict部分 >>> dict = {'x': 'A', 'y': 'B'} >>> [k + '=' + v for k, v in dict.items()] >>> ['x=A', 'y=B']
生成器(generator)
概念通过列表生成式, 直接生成的list, 当量大时耗内存, 使用生成器按照逻辑边循环边计算, 节省空间
-
简单方式
# 直接把列表生成式改成 简单的generator 区别: [] => () >>> g = (x * x for x in [1, 2]) >>> <generator object <genexpr> at 0x1022ef630>
-
遍历每一项
# 每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误 >>> next(g) >>> 1 >>> next(g) >>> 4 # 没有了就结束 >>> next(g) >>> StopIteration # 通过 for...in...(常用) >>> for n in g >>> 1 4
-
斐波那契
# 相当于 t = (0, 0, 1) => a = t[0] >>> n, a, b = 0, 0, 1 # 关键字yield, 生成generator 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和函数的执行流程不一样, 函数是顺序执行,遇到return语句或者最后一行函数语句就返回, 变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
迭代器
概念: 可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator, 生成器都是Iterator对象,
但list、dict、str虽然是Iterable(可迭代),却不是Iterator(可迭代对象)
-
dict, list, tuple, str数据类型为何不是Iterator ?
Iterator对象表示的是一个
数据流
, 被next()函数调用时不断返回下一个数据, 直到没有数据时抛出StopIteration
错误, 我们不能提前知道序列长度, 所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算.Iterator甚至可以表示一个无限大的数据流
,例如全体自然数。而使用list是永远不可能存储全体自然数的. -
判断是否Iterator对象
>>> from collections import Iterator >>> isinstance([], Iterator) # 不是迭代器 >>> False
-
使用
iter()函数
变成迭代器Iterator# 使用内置iter()函数, list、dict、str等Iterable变成Iterator >>> isinstance(iter([]), Iterator) >>> True
-
小结:
~ 凡是可作用于for循环的对象都是Iterable类型;
~ 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
~ 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
~ for循环本质上就是通过不断调用next()函数实现的
it = iter([1, 2, 3]) while True: try: x = next(it) print(x) except StopIteration: print('done') break
函数式编程
- 由于python允许使用变量, 故而python不是纯函数式(固定输入固定输出)编程语言
高阶函数
函数名也是变量, 只是函数名已经指定到函数
-
概念: 一个函数接受另外一个函数作为参数, 高阶函数
>>> def add(x, y, f): ... return f(x) + f(y) >>> add(-5, 1, abs) >>> 6
map/reducer
-
map: 接受两个参数,
函数
和Iterable
,map将传入的函数依次作用到序列的每个元素
,结果返回新的Iterator
>>> def Fn(x): ... return x * x # 第一个参数是函数, 第二个参数是Iterable >>> map(Fn, [1, 2, 3]) # 直接输出的是map对象 <map object at 0x000001B9AF26F4E0> # Iterator是惰性序列,通过list()函数让它把整个序列都计算出来并返回一个list >>> print(list(map(Fn, [1, 2, 3]))) [1, 4, 9]
-
reduce: 接受
函数
和Iterable
, 这个函数必须接收两个参数,reduce
把结果继续和序列的下一个元素做累积计算, 返回结果不是Iterator- 效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
>>> from functools import reduce >>> def add(x, y): ... return x * 10 + y ... >>> reduce(add, [1, 2, 3]) 123
- 效果就是:
-
自己实现原生的
int
函数from functools import reduce num = {'1': 1, '2': 2, '3'} def str2int(s): def fn(x, y): return x * 10 + y def char2num(x): return num[x] return reduce(fn, map(char2num, s)) # 借用map和reduce, 此时输入'123', 调用str2int函数, 会转化成数字123
filter
-
概念: 内置高阶函数, 用于过滤序列, 返回
Iterator
def no_empty(s): return s and s.strip() l = filter(no_empty, ['a', '', None, 'B']) # 返回的是惰性序列, 使用list()获得结果, 并返回list list(l)
埃氏筛法: 求解素数, 2素数, 2倍数删除, 3素数, 3的倍数删除, 一次下去, 求得所有素数
sorted
-
概念: 内置高阶函数, 对list进行排序, 可以接收一个key函数来实现自定义的排序, 返回排序之后的list
# 基本用法 >>> sorted([10, -10, 2, 5]) [-10, 2, 5, 10] # 作为高阶函数用法, 按key作用后的结果排序 sorted([5, -4, 3], key=abs) [3, -4, 5]
-
字符串排序, 按
ASCII
码进行排序# ascii码里面, b > A >>> sorted(['bob', 'Amazing', 'Test']) ['Amazing', 'Test', 'bob'] # 全部转化为小写再比较 >>> sorted(['bob', 'Amazing', 'Test'], key=str.lower) ['Amazing', 'bob', 'Test']
反向排序, 可以传入第三个参数
reverse=True
返回函数
-
例子
# 类似js闭包, 内部函数可以得到外部函数的局部变量 def lazy_sum(*num): def get_sum(): sum = 0 for key in num: sum = sum + key return sum return get_sum # 每次调用都是返回新的函数 f = lazy_sum(1, 2, 3) # 再次调用才得出结果 f()
闭包
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
```py
# 所引用的变量i全部变成了3
def count():
l = []
for i in range(1, 4):
def fn():
return i * i
l.append(fn)
return l
f1, f2, f3 = count()
>>> f1()
9
>>> f2()
9
>>> f3()
9
# 改进
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
```
匿名函数
-
概念: 关键字
lambda
表示匿名函数,冒号前面的x表示函数参数, 只能有一个表达式, 表达式结果就是return的返回值>>> f = lambda x: x * x # 等价 >>> def f(x): ... return x * x
装饰器(decorator)
概念: 在代码运行期间动态增加功能, 本质上是返回函数的高阶函数
-
__name__属性
,可以拿到函数的名字def fn(x): return x f = fn # 通过__name__拿到函数名字 >>> fn.__name__ 'fn' >>> f.__name__ 'fn'
-
例子
# 不带参数的decorator import functools def log(func): # 为了最终返回的decorator的__name__指向的还是func而不会变成wrapper @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper # 带参数的decorator import functools # 带参数的decorator, 在原来函数中可以做其他的操作 def log(text): def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorator # 调用 def fn(x): print('调用', x) fn = log('log日志')(fn) fn(88)
偏函数
-
概念: 偏函数是由functools模块提供,
functools.partial
把一个函数的某些参数固定住(设置默认值), 返回新的函数>>> import functools >>> int2 = functools.partial(int, base = 16) # 预设有10, 如果都不比是10大就选择10 >>> max2 = functools.partial(max, 10) >>> max2(1, 2, 3) 10
模块
概念: 一个.py文件一个模块, 跨模块不会出现变量名冲突
-
包: 为避免模块名冲突, 每个包下面都有
__init__.py
文件# 引入模块, myCompany是顶层包, a是a.py模块, test是a.py的方法 import myCompany.a myCompany.a.test()
自己创建模块时要注意命名,不能和Python自带的模块名称冲突
[图片上传失败...(image-f76f68-1547169230449)]
使用模块
-
例子
# 任何模块代码的第一个字符串都被视为模块的文档注释; 'my first module' # 代码作者 __author__ = 'bin.wang' import sys def test(): args = sys.argv print(args) if len(args) == 1: print('hello world') elif len(args) == 2: print('hello, %s' % args[1]) else: print('too many arguments') # 当运行到这个模块时, __name__会更改为__main__, 当导入时判断语句为False # 这是常见的运行测试 if __name__ == '__main__': test()
-
作用域
- 变量的命名规则
模块内部使用:_XXX
和__XXX
公开的函数:XXX
特殊变量:__XXX__
, 直接引用但一般特殊用途,__name__
- 变量的命名规则
第三方模块(类比js的npm)
安装第三方模块, 包管理工具pip
-
安装pip
win下安装: 安装python时, 勾选
pip和Add python.exe to Path
(但我是勾选了Add Python 3.7 to PATH
)mac系统下安装: 记得使用pip3
pip安装第三方模块: 一个一个安装费时, 且需要考虑兼容性
-
安装Anaconda(需要研究)
概念: 基于Python的数据处理和科学计算平台, 内置许多第三方库, MySQL驱动程序,Web框架Flask,科学计算Numpy等.
Anaconda会把系统Path中的python指向自己自带的Python,并且,Anaconda安装的第三方模块会安装在Anaconda自己的路径下,不影响系统已安装的Python目录
地址:
https://www.anaconda.com/download/
面向对象编程
- 概念: OOP, 设计思想, 封装继承和多态
类和实例
-
类和实例: 类实例化出对象, 对象之间方法一致, 数据不一样
# 关键字class, 类名大写, object类, 代表该类是从object类继承下来 class Student(object): # 在创建实例时就把, name和score绑定上去, 类比jsconstruct # self代表实例本身(类比js的this), 创建实例就需要传入name和score def __init__(self, name, score): # 加上__, 变成私有变量, 外部就不能访问 self.__name = name self.__score = score def print_score(self): print('%s: %s' % (self.__name, self.score)) # 调用, 创建实例, 调用方法 ben = Student('ben', 99) ben.print_score()
访问限制
- 概念: 在属性名称前加上
__
, 变成私有变量, 这样外部就不能访问了(_XX
还能访问, 但也约定是私有变量), 注意区分__XXX__
, 特殊变量, 能直接访问
继承和多态
```py
class Animal(object):
def run(self):
print('animal is running')
# 继承, 子类方法覆盖父类同类方法
class Cat(Animal):
def run(self):
print('cat is running')
# 实例化的对象跟str, list等一样都属于某个数据类型, 所以也能做类型判断
cat = Cat()
isinstance(cat, Cat) # True
isinstance([], list) # True
```
获取对象信息(判断对象类型)
type()函数:
type(123)
-><class 'int'>
isinstance()
-
dir(): 获得一个对象的所有属性和方法, 返回一个包含字符串的list
>>> dir(list) ['__add__', '__len__',..., 'copy'] # 调用python内置的len(list), 其实就是调用了__len__方法, 跟list.__len__() 是一样的
-
getattr(), hasattr(), setattr()
class MyObject(object): def __init__(self): self.x = 9 def power(self): return self.x * self.x obj = MyObject() hasattr(obj, 'x') # True hasattr(obj, 'power') # True setattr(obj, 'y', 2) hasattr(obj, 'y') # True
实例属性和类属性
- 给实例绑定属性, 方法, 给类绑定属性, 方法
from types import MethodType # 定义类 class Student(object): # 给类加属性 gender = '男' def __init__(self, name): self.name = name # 类绑定方法, 实例化的对象都可以使用 def setScore(self, score): self.score = score Student.setScore = setScore # 实例化, 绑定的属性可方法, 只适用于本实例 s = Student('Ben') # 实例绑定属性, 优先级大于类 s.gender = '女' def getAge(self, age): self.age = age s.getAge = MethodType(getAge, s) s.getAge(18) s.age # 18, 且此实例不会影响别的实例
面向对象高级编程(进阶版本)
使用slots
-
概念: 限制实例的属性, 继承它的子类不起作用, 但是子类设置了
__slots__
, 则会把父类的slots一起继承class Student(object): # 用tuple的方式限定实例可增加的属性 __slots__ = ('name', 'age') s = Student() # AttributeError: 'Student' object has no attribute 'gender' s.gender = '男'
使用@property(不太理解)
-
概念: @property装饰器, 把一个方法变成属性调用的
# score是可读写属性, 那么是只读属性 class Student(object): # property装饰器, 方法变成属性一样使用 @property def score(self): return self._score # score的设置, 像设置属性一样使用里面的方法 @score.setter def score(self, val): self._score = val # 只设置了读, 没设置写入, 就是只读 @property def name(self): return self._name # 实例 s = Student() s.score = 60 print(s.score) # 60
多重继承
-
概念: 多重继承,一个子类就可以同时获得多个父类的所有功能
class Animal(object): pass class Runable(object): pass # 狗继承了两个父类 class Dog(Animal, Runable): pass
MixIn: 上述的继承多个的设计, 目的就是给一个类增加多个功能
定制类
-
__str__
: 使得输出更易理解class Student(object): def __init__(self, name): self.name = name def __str__(self): return 'Student object (name: %s)' % (self.name) __repr__ = __str__ # 原本: <__main__.Student object at 0x00000244C4994CF8> # 现在: Student object (name: test) print(Student('test'))
-
__iter__
: 使得类返回迭代对象, 使得类可迭代(不太理解作用)class Fib(object): def __init__(self): self.a, self.b = 0, 1 def __iter__(self): return self def __next__(self): self.a, self.b = self.b , self.a + self.b # 退出循环条件 if self.a > 10000: raise StopIteration return self.a for key in Fib(): print(key)
-
__getitem__
: 使得类除了forin迭代之外, 还可以根据下标取值class Fib(object): def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b return a # 3, 能根据下标取值 print(Fib()[3])
__getattr__
__call__
枚举类
-
概念: Enum可以把一组相关常量定义在一个class中,且class不可变,成员可以直接比较
from enum import Enum, unique # value属性则是自动赋给成员的int常量,默认从1开始计数 Month = Enum('Month', ('Jan', 'Feb')) # 如果value的属性想自定义, 则需要用到@unique装饰器 @unique class Weekday(Enum): Sun = 0 Mon = 1 #访问 for name, member in Weekday.__members__.items(): print(name, '=>', member, ',', member.value) # Sun => Weekday.Sun , 0 # 根据value获取值 print(Weekday.Sun.value) # 0 # 根据value也可以反推member print(weekday(0)) # Sun
使用元类
- 概念
错误, 调试和测试
错误处理
try...except...finally...
-
try某步报错立马跳转到except, 最后finally都会执行
try: print('try...') r = 10 / int('2') print('result:', r) except ValueError as e: print('值错误:', e) except ZeroDivisionError as e: print('0做分母:', e) # try执行不报错的话, 才会执行else else: print('no error!') finally: print('finally...') print('END')
python 的错误也是class, 错误类型都继承自
BaseException
, 且捕获该类型错误, 还会顺便捕获其子类错误常见的错误类型和继承关系:
https://docs.python.org/3/library/exceptions.html#exception-hierarchy
调用栈
- 概念: 分析错误的调用栈信息,才能定位错误的位置。
记录错误
-
概念: 内置的logging模块可以记录错误信息
import logging def foo(s): return 10 / int(s) def bar(s): return foo(s) * 2 def main(): # 直接通过logging捕获错误, 但是不会退出程序的执行, END还会执行 try: bar('0') except Exception as e: logging.exception(e) main() print('END')
抛出错误
- 概念
未完待续...