python学习笔记
1、下载 https://www.python.org/ftp/python/3.7.0/python-3.7.0-amd64.exe
2、安装时注意勾选添加环境变更
3、命令窗输入 python,如果有相关版本信息就成功,此时进入命令模式,退出输入exit()
linux下直接运行方法:
1、源文件上首行为
#!/usr/bin/env python3
2、chmod a+x test.py
3、./test.py
\转义符,本身也要
引号中含有其它引号要用转义符,可以在字符串前加r就不用转义,如s3 = r'Hello, "Bart"'
'''可以写多行字符串,如
print('''line1
line2
line3''')
注释用#
整数的地板除//得出只有整数,比如10//3=3
ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符
Python的字符串类型是str,在内存中以Unicode表示
bytes类型: X=b'ABC'
以Unicode表示的str通过encode()方法可以编码为指定的bytes,如: '中国'.encode('utf-8')
把bytes变为str,就需要用decode()方法: b'123'.decode('utf-8')
如果bytes中只有一小部分无效的字节,可以传入errors='ignore'忽略错误的字节:b'\xe4\xb8\xad\xff'.decode('utf-8',error='ignore')
len('中文'):2
len('中文'.encode('utf-8')):6
%s表示用字符串替换,%d表示用整数替换,有几个%?占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个%?,括号可以省略。
'Hi, %s, you have $%d.' % ('Michael', 1000000)
转义,用%%来表示一个%
classmates = ['Michael', 'Bob', 'Tracy']
还可以用-1做索引,直接获取最后一个元素,往list中追加元素到末尾:
classmates.append('Adam')
把元素插入到指定的位置 classmates.insert(1, 'Jack')
要删除list末尾的元素 classmates.pop()
要删除指定位置的元素用pop(i)方法,i是索引位置
元组tuple
classmates = ('Michael', 'Bob', 'Tracy')
Python在显示只有1个元素的tuple时,也会加一个逗号,,如t = (1,)
if <条件判断1>:
<执行1>
elif <条件判断2>:
<执行2>
elif <条件判断3>:
<执行3>
else:
<执行4>
字符不能和整数直接比较,字符转整数:int()
if bmi >= 18.5 and bmi < 25:
range(5)生成的序列是从0开始小于5的整数
for name in names:
print(name)
while n > 0:
sum = sum + n
n = n - 2
break\continue和JAVA一样
d={'a':1,'b':2}
判断KEY是否存在'a' in d
二是通过dict提供的get()方法,如果key不存在,可以返回None,或者自己指定的value:d.get('Thomas', -1)
要删除一个key,用pop(key)方法,要增加KV,直接用d[key]='xxx'
牢记的第一条就是dict的key必须是不可变对象。
set中存储KEY,不存VALUE,且KEy不能重复
要创建一个set,需要提供一个list作为输入集合:
>>> s = set([1, 2, 3])
通过add(key)方法可以添加元素到set中
通过remove(key)方法可以删除元素
两个set可以做数学意义上的交集、并集等操作: 交集s1&s2,并集s1|s2
通过help(abs)查看abs函数的帮助信息
而max函数max()可以接收任意多个参数,,并返回最大的那个
int() float() str() bool()
函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:
>>> a = abs # 变量a指向abs函数
>>> a(-1) # 所以也可以通过a调用abs函数
定义一个函数要使用def语句,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回。
实际上pass可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
Python的函数返回多值其实就是返回一个tuple,但写起来更方便。
ax2 + bx + c = 0
ax2+2a cx + c2=0
默认参数,如:
def power(x, n=2):
定义默认参数要牢记一点:默认参数必须指向不变对象! 反例:def add_end(L=[]) 纠正:def add_end(L=None)
定义可以变参数:def calc(*numbers): 调用calc(1,2,3),参数送入list 的话,使用calc(*list), *nums表示把nums这个list的所有元素作为可变参数传进去
关键字参数:
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
调用:person('Adam', 45, gender='M', job='Engineer')
送字典时可以简化为person('Jack', 24, **extra),与可变参数类似
命名关键字参数:只接收city和job作为关键字参数。这种方式定义的函数如下:def person(name, age, *, city, job):
参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
对于任意函数,都可以通过类似func(*args, **kw)的形式调用它
>>> args = (1, 2, 3)
>>> kw = {'d': 88, 'x': '#'}
>>> f2(*args, **kw)
a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}
解决递归调用栈溢出的方法是通过尾递归优化
尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环。
切片:
顺切 L[n:m],n为0可省略,表示取序号从n开始,不包含m的序列
逆切 L[-n:-m] 表示取序号从-m开始,不包含-n的序列,m省略则取从最尾往前取n个数,
Python内置的enumerate函数可以把一个list变成索引-元素对
>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
列表生成式:
1、生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用list(range(1, 11))
2、复杂一些 [x*x for x in range(1,11)]
3、过滤一下奇数 [x*x for x in range(1,11)if x%2==0]
3、双层[x+y for x in range(1,11) for y in range(1,11)]
生成器:generator
g = (x * x for x in range(10))
获得下个返回值:next(g),如果超过会报StopIteration的错误。
创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它
如果一个函数定义中包含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的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中
普通函数和generator函数,普通函数调用直接返回结果
generator函数的“调用”实际返回一个generator对象
以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function。
一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数:def fun1(fun2,param)
map(fun,Iterator):list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
要先引入reduce才能使用
from functools import reduce
filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
filter()的作用是从一个序列中筛出符合条件的元素。由于filter()使用了惰性计算,所以只有在取filter()结果的时候,才会真正筛选并每次返回下一个筛出的元素。
忽略大小写排序:sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
反向排序:sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
匿名函数lambda有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
lambda x,y: x+y
这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)
本质上,decorator就是一个返回函数的高阶函数。它将函数作为参数,再将函数封装成一个新函数进行返回,例如:
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
Python的@语法,把decorator置于函数的定义处:
@log
def now():
print('2015-3-25')
把@log放到now()函数的定义处,相当于执行了语句:
now = log(now)
__name__可以打印出函数的名称
functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
int2 = functools.partial(int, base=2)
一个.py文件就称之为一个模块(Module)
Python又引入了按目录来组织模块的方法,称为包(Package)
每一个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。__init__.py可以是空文件,也可以有Python代码,因为__init__.py本身就是一个模块,而它的模块名就是mycompany
模块名不要和系统模块名冲突,最好先查看系统是否已存在该模块,检查方法是在Python交互环境执行import abc,若成功则说明系统存在此模块。
python代码的基本格式:
#!/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()
类似__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author__,__name__就是特殊变量,hello模块定义的文档注释也可以用特殊变量__doc__访问,我们自己的变量一般不要用这种变量名;
类似_xxx和__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc,__abc等;
安装Pillow的命令就是:pip install Pillow
装上Anaconda,就相当于把数十个第三方模块自动安装好了
要添加自己的搜索目录,有两种方法:
一是直接修改sys.path,添加要搜索的目录:
>>> import sys
>>> sys.path.append('/Users/michael/my_py_scripts')
第二种方法是设置环境变量PYTHONPATH,该环境变量的内容会被自动添加到模块搜索路径中。
类:
定义一个特殊的__init__方法,相当构造函数
在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self
要定义一个方法,除了第一个参数是self外,其他和普通函数一样。要调用一个方法,只需要在实例变量上直接调用,除了self不用传递,其他参数正常传入
和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同。
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__
判断一个变量是否是某个类型可以用isinstance()判断:
>>> isinstance(a, list)
True
动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
Python的“file-like object“就是一种鸭子类型。
我们来判断对象类型,使用type()函数:
基本类型都可以用type()判断:
>>> type(123)
<class 'int'>
>>> type('str')
<class 'str'>
>>> type(None)
<type(None) 'NoneType'>
能用type()判断的基本类型也可以用isinstance()判断
并且还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是list或者tuple:
>>> isinstance([1, 2, 3], (list, tuple))
True
如果要获得一个对象的所有属性和方法,可以使用dir()函数
配合getattr()、setattr()以及hasattr(),我们可以直接操作一个对象的状态
>>> hasattr(obj, 'x') # 有属性'x'吗?
True
>>> obj.x
9
>>> hasattr(obj, 'y') # 有属性'y'吗?
False
>>> setattr(obj, 'y', 19) # 设置一个属性'y'
>>> hasattr(obj, 'y') # 有属性'y'吗?
True
>>> getattr(obj, 'y') # 获取属性'y'
19
删除对象的一个属性:del s.name
创建了一个class的实例后,我们可以给该实例绑定任何属性和方法
>>> def set_age(self, age): # 定义一个函数作为实例方法
... self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
>>> s.set_age(25) # 调用实例方法
>>> s.age # 测试结果
25
给类绑定方法:
Student.set_score = set_score
想要限制实例的属性,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:
在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__
Python内置的@property装饰器就是负责把一个方法变成属性调用的:
使用@property,方法内的属性记得加_
只读属性必须通过其它属性计算得值,或返回常量
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
只需要定义好__str__()方法,返回一个好看的字符串就可以了
直接显示变量调用的不是__str__(),而是__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。
偷懒方法: __repr__ = __str__
如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值
要表现得像list那样按照下标取出元素,需要实现__getitem__()方法
调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值
定义一个__call__()方法,就可以直接对实例进行调用
能被调用的对象就是一个Callable对象,比如函数和我们上面定义的带有__call__()的类实例
@unique装饰器可以帮助我们检查保证Enum没有重复值
先定义metaclass,就可以创建类,最后创建实例。
让我们用一个例子来看看try的机制:
try:
print('try...')
r = 10 / int('2')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally...')
print('END')
启动Python解释器时可以用-O参数来关闭assert:python -O err.py
启动Python的调试器pdb,让程序以单步方式运行,可以随时查看运行状态
python -m pdb err.py
输入l查看代码,输入p 变量名 来看值
import pdb
...
pdb.set_trace() # 运行到这里会自动暂停
目前比较好的Python IDE有:
Visual Studio Code:https://code.visualstudio.com/,需要安装Python插件。
PyCharm:http://www.jetbrains.com/pycharm/
关闭后,你可以把所有的assert语句当成pass来看
assert n != 0, 'n is zero!'
assert的意思是,表达式n != 0应该是True,否则,根据程序运行的逻辑,后面的代码肯定会出错。
logging.info()就可以输出一段文本。运行,发现除了ZeroDivisionError,没有任何信息。怎么回事?
别急,在import logging之后添加一行配置再试试:
import logging
logging.basicConfig(level=logging.INFO)
logging允许你指定记录信息的级别,有debug,info,warning,error等几个级别,当我们指定level=INFO时,logging.debug就不起作用了。同理,指定level=WARNING后,debug和info就不起作用了。
在线安装:pip install schedule
安装离线包步骤:
1、到页面下载TZ包
2、解压后分别进入目录,执行python setup.py build; python setuo.py install
解压用gunzip schedule-0.6.0.tar.gz 后转成tar文件,再解压用 tar -xf -f schedule-0.6.0.tar
其实可以一步到位:tar -zxf -f schedule-0.6.0.tar.gz