前言
非常感谢廖雪峰老师的免费开源的Python3教程:因为教程实在写的太好了!
花了点时间将廖老师的文章,调整、排序,浓缩 ,以便于对于掌握其它编程语言的小伙伴快速上手Python3!
原教程地址:https://www.liaoxuefeng.com/ 作者:廖雪峰
一、介绍
Python是著名的“龟叔”Guido van Rossum在1989年圣诞节期间,为了打发无聊的圣诞节而编写的一个编程语言。
在Windows上安装Python
首先,根据你的Windows版本(64位还是32位)从Python的官方网站下载Python 3.6对应的64位安装程序或32位安装程序(网速慢的同学请移步国内镜像),然后,运行下载的EXE安装包。
Python解释器:当我们编写Python代码时,我们得到的是一个包含Python代码的以.py为扩展名的文本文件。要运行代码,就需要Python解释器去执行.py文件。*(官方版本:cPython)
二、数据结构:
整数、浮点数、字符串、布尔值、空值(none)、变量、常量。
list :[ ] 列表 、 set:([]) 不重复无序的集合、tuple:( ) 不可变列表 dict:{ } 字典、相当于java中的map。
三、函数
函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”
说是可以返回多个值,返回的仍然是单一值:只是返回了一个tuple!
https://docs.python.org/3/library/functions.html 内置函数参考
四、参数:
def person(*age): 可变参数、是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数
def person(name, age, **kw):关键字参数,允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict:
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra) name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
def person(name, age, *, city, job):如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。
>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer
def person(name, age=2):默认参数。
def person(name, age):必选参数。
参数组合:参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
五、迭代
默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.values(),如果要同时迭代key和value,可以用for k, v in d.items()。
for 循环变量 in 要被遍历的内容':
那么,如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断
六、列表生成式
即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式
[[x * x for x in range(1, 11)]]
七、生成器
一边循环一边计算的机制,从从而节省大量的空间而节省大量的空间,称为生成器:generator。
创建一个generator,有很多种方法。
第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。
把list、dict、str等Iterable变成Iterator可以使用iter()函数:
>> isinstance(iter('abc'), Iterator)
True
八、函数式编程
一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数!
def add(x, y, f):
return f(x) + f(y)
九、重要函数讲解
map/reduce函数:
map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回~
把这个list所有数字转为字符串:
>>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
['1', '2', '3', '4', '5', '6', '7', '8', '9']
reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算
>>> from functools import reduce
>>> def add(x, y):
......... return x + y
>>> reduce(add, [1, 3, 5, 7, 9])
25
filter函数
Python内建的filter()函数用于过滤序列。
sorted函数
Python内置的sorted()函数就可以对list进行排序。
偏函数
在介绍函数参数的时候,我们讲到,通过设定参数的默认值,可以降低函数调用的难度。而偏函数也可以做到这一点
>>> import functools
>>> int2 = functools.partial(int, base=2) 转换成二进制的整数
>>> int2('1000000')
64
type函数
既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义:
要创建一个class对象,type()函数依次传入3个参数:
- class的名称;
- 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
- class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
>>> def fn(self, name='world'): # 先定义函数
... print('Hello, %s.' % name)
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>
十、闭包:
这种在函数中定义函数,又定义了函数,叫闭包,并让定义函数作为返回值,不需要立刻求值,而是在后面的代码中,根据需要再计算。
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
十一、匿名函数:
关键字lambda表示匿名函数,冒号前面的x表示函数参数,匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]
十二、装饰器:
代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
@log('execute')
def now():
print('2015-3-25')
把@log放到now()函数的定义处,相当于执行了语句:
装饰器也是可以传入参数的:
@log('execute')
def now():
print('2015-3-25')
相当于:>>> now = log('execute')(now)
十三、模块
说明结尾两行代码:
if __name__=='__main__':
test()
当我们在命令行运行hello模块文件时,Python解释器把一个特殊变量__name__置为__main__,而如果在其他地方导入该hello模块时,if判断将失败,因此,这种if测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试。
我们可以用命令行运行hello.py看看效果:
$ python3 hello.py
Hello, world!
$ python hello.py MichaelHello, Michael!
十四、作用域
类似_xxx和__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc,__abc等;
我们在模块里公开greeting()函数,而把内部逻辑用private函数隐藏起来了,这样,调用greeting()函数不用关心内部的private函数细节,这也是一种非常有用的代码封装和抽象的方法,即:
外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。
十五、类
类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。
通过定义一个特殊的__init__方法,在创建实例的时候,就把name,score等属性绑
上去:
类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类
定义好了Student类,就可以根据Student类创建出Student的实例,创建实例是通过类名+()实现的:
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))
注意:Student().name 是外部实例变量不等于Class Student 的内部name属性。
十六、继承和多态
class Dog(Animal): Animal为父类,这就是继承
pass
多重继承:直接在小括号里,继承多个类就行了~
class Dog(Mammal, Runnable): Mammal, Runnable为两个父类,
pass
高级编程语言多态都是一个概念,python也不例外,重写父类的方法就是多态!
十七、实例属性和类属性
s.set_age = MethodType(set_age, s) # 给实例绑定一个方法,在新建一个实例就不生效了。下面还可以为class绑定方法,
这样在新建实例也可以生效了!
>>> def set_score(self, score):
... self.score = score
>>> Student.set_score = set_score
限制实例的属性怎么办?比如,只允许对Student实例添加name和age属性:
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
__str__:类似于java中的重写toString方法,python 在调用print方法的时候,会执行你定义的~
__iter__:如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环
__getitem__:实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,要表现得像list那样按照下标取出元素,需要实现__getitem__()方法
__getattr__:调用不存在的score属性,就有问题了:要避免这个错误,除了可以加上一个score属性外,Python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性,返回函数也是完全可以的。
__call__:任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用
十八、枚举类
Enum可以把一组相关常量定义在一个class中,且class不可变,而且成员可以直接比较。
十九、metaclass(元类)
除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。
metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,你不会碰到需要使用metaclass的情况,所以,以下内容看不懂也没关系,因为基本上你不会用到。
metaclass可以给我们自定义的MyList增加一个add方法:
# metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)
有了ListMetaclass,我们在定义类的时候还要指示使用ListMetaclass来定制类,传入关键字参数metaclass:
class MyList(list, metaclass=ListMetaclass):
pass
当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.__new__()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。
__new__()方法接收到的参数依次是:
- 当前准备创建的类的对象;
- 类的名字;
- 类继承的父类集合;
- 类的方法集合。
测试一下MyList是否可以调用add()方法:
>>> L = MyList()
>>> L.add(1)
>> L
[1]
而普通的list没有add()方法:
>>> L2 = list()
>>> L2.add(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'add'
动态修改有什么意义?直接在MyList定义中写上add()方法不是更简单吗?正常情况下,确实应该直接写,通过metaclass修改纯属变态。
二十、异常处理
高级语言通常都内置了一套try...except...finally...的错误处理机制,Python也不例外。
try:
print('try...')
r = 10 / 0
print('result:', r)
except ZeroDivisionError as e:
print('except:', e)
finally:
print('finally...')
print('END')
调用栈
如果错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获,打印一个错误信息,然后程序退出。来看看err.py:
# err.py:def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
bar('0')
main()
执行,结果如下:
$ python3 err.py
Traceback (most recent call last):
File "err.py", line 11, in <module>
main()
File "err.py", line 9, in main
bar('0')
File "err.py", line 6, in bar
return foo(s) * 2
File "err.py", line 3, in foo
return 10 / int(s)
ZeroDivisionError: division by zero
出错并不可怕,可怕的是不知道哪里出错了。解读错误信息是定位错误的关键。我们从上往下可以看到整个错误的调用函数链:
错误信息第1行:
Traceback (most recent call last):
告诉我们这是错误的跟踪信息。
第2~3行:
File "err.py", line 11, in <module>
main()
调用main()出错了,在代码文件err.py的第11行代码,但原因是第9行:
File "err.py", line 9, in main
bar('0')
调用bar('0')出错了,在代码文件err.py的第9行代码,但原因是第6行:
File "err.py", line 6, in bar
return foo(s) * 2
原因是return foo(s) * 2这个语句出错了,但这还不是最终原因,继续往下看:
File "err.py", line 3, in foo
return 10 / int(s)
原因是return 10 / int(s)这个语句出错了,这是错误产生的源头,因为下面打印了:
ZeroDivisionError: integer division or modulo by zero
根据错误类型ZeroDivisionError,我们判断,int(s)本身并没有出错,但是int(s)返回0,在计算10 / 0时出错,至此,找到错误源头。
出错的时候,一定要分析错误的调用栈信息,才能定位错误的位置。
抛出错误
如果要抛出错误,首先根据需要,可以定义一个错误的class,选择好继承关系,然后,用raise语句抛出一个错误的实例:
待续
打赏
微信扫一扫,打赏作者吧~