本文首发在 我的博客html
Python 是一门运用很普遍的语言,自动化脚本、爬虫,甚至在深度学习领域也都有 Python 的身影。做为一名前端开发者,也了解 ES6 中的不少特性借鉴自 Python (好比默认参数、解构赋值、Decorator等),同时本文会对 Python 的一些用法与 JS 进行类比。不论是提高本身的知识广度,仍是更好地迎接 AI 时代,Python 都是一门值得学习的语言。前端
在 Python 中,最经常使用的可以直接处理的数据类型有如下几种:python
除此以外,Python 还提供了列表[list]、字典[dict] 等多种数据类型,这在下文中会介绍。es6
与 JS 十分相似,python 也能实现不一样数据类型间的强制与隐式转换,例子以下:编程
强制类型转换:数组
int('3') # 3
str(3.14) # '3.14'
float('3.14') # 3.14
# 区别于 JS 只有 Number 一种类型,Python 中数字中的不一样类型也能相互强制转换
float(3) # 3.0
bool(3) # True
bool(0) # False复制代码
隐式类型转换:数据结构
1 + 1.0 # 2.0
1 + False # 1
1.0 + True # 2.0
# 区别于 JS 的 String + Number = String, py 中 str + int 会报错
1 + '1' # TypeError: cannot concatenate 'str' and 'int' objects复制代码
此外写代码的时候常常会须要判断值的类型,能够 使用 python 提供的 type() 函数获取变量的类型,或者使用 isinstance(x, type) 来判断 x 是否属于相应的 type 类型。闭包
type(1.3) == float # True
isinstance('a', str) # True
isinstance(1.3, int) # False
isinstance(True, bool) # True
isinstance([], list) # True
isinstance({}, dict) # True复制代码
集合是指包含一组元素的数据结构,有序集合即集合里面的元素是是按照顺序排列的,Python 中的有序集合大概有如下几类:list, tuple, str, unicode。app
Python 中 List 类型相似于 JS 中的 Array,async
L = [1, 2, 3]
print L[-1] # '3'
L.append(4) # 末尾添加元素
print L # [1, 2, 3, 4]
L.insert(0, 'hi') # 指定索引位置添加元素
print L # ['hi', 1, 2, 3, 4]
L.pop() # 末尾移除元素 L.pop(2) ?????? 2 ???
print L # ['hi', 1, 2, 3]复制代码
tuple 类型是另外一种有序的列表,中文翻译为“ 元组 ”。tuple 和 list 很是相似,可是,tuple 一旦建立完毕,就不能修改了。
t = (1, 2, 3)
print t[0] # 1
t[0] = 11 # TypeError: 'tuple' object does not support item assignment
t = (1)
print t # 1 t 的结果是整数 1
t = (1,) # 为了不出现如上有歧义的单元素 tuple,因此 Python 规定,单元素 tuple 要多加一个逗号“,”
print t # (1,)复制代码
Python 中的 dict 类型相似于 JS 中的 {} (最大的不一样是它是没有顺序的), 它有以下特色:
d = {
'a': 1,
'b': 2,
'c': 3
}
print d # {'a': 1, 'c': 3, 'b': 2} 能够看出打印出的序对没有按正常的顺序打出
# 遍历 dict
for key,value in d.items():
print('%s: %s' % (key,value))
# a: 1
# c: 3
# b: 2复制代码
有的时候,咱们只想要 dict 的 key,不关心 key 对应的 value,并且要保证这个集合的元素不会重复,这时,set 类型就派上用场了。set 类型有以下特色:
s = set(['A', 'B', 'C', 'C'])
print s # set(['A', 'C', 'B'])
s.add('D')
print s # set(['A', 'C', 'B', 'D'])
s.remove('D')
print s # set(['A', 'C', 'B'])复制代码
在介绍完 Python 中的有序集合和无序集合类型后,必然存在遍历集合的 for 循环。可是和其它语言的标准 for 循环不一样,Python 中的全部迭代是经过 for ... in 来完成的。如下给出一些经常使用的迭代 demos:
索引迭代:
L = ['apple', 'banana', 'orange']
for index, name in enumerate(L): # enumerate() 函数把 ['apple', 'banana', 'orange'] 变成了相似 [(0, 'apple), (1, 'banana'), (2, 'orange')] 的形式
print index, '-', name
# 0 - apple
# 1 - banana
# 2 - orange复制代码
迭代 dict 的 value:
d = { 'apple': 6, 'banana': 8, 'orange': 5 }
print d.values() # [6, 8, 5]
for v in d.values()
print v
# 6
# 8
# 5复制代码
迭代 dict 的 key 和 value:
d = { 'apple': 6, 'banana': 8, 'orange': 5 }
for key, value in d.items()
print key, ':', value
# apple : 6
# banana: 8
# orange: 5复制代码
Python 提供的切片操做符相似于 JS 提供的原生函数 slice()。有了切片操做符,大大简化了一些原来得用循环的操做。
L = ['apple', 'banana', 'orange', 'pear']
L[0:2] # ['apple', 'banana'] 取前 2 个元素
L[:2] # ['apple', 'banana'] 若是第一个索引是 0,能够省略
L[:] # ['apple', 'banana', 'orange', 'pear'] 只用一个 : ,表示从头至尾
L[::2] # ['apple', 'orange'] 第三个参数表示每 N 个取一个,这里表示从头开始,每 2 个元素取出一个来复制代码
若是要生成 [1x1, 2x2, 3x3, ..., 10x10] 怎么作?方法一是循环:
L = []
for x in range(1, 11):
L.append(x * x)复制代码
可是循环太繁琐,而列表生成式则能够用一行语句代替循环生成上面的 list:
# 把要生成的元素 x * x 放到前面,后面跟 for 循环,就能够把 list 建立出来
[x * x for x in range(1, 11)]
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]复制代码
列表生成式的 for 循环后面还能够加上 if 判断(相似于 JS 中的 filter() 函数),示例以下:
[x * x for x in range(1, 11) if x % 2 == 0]
# [4, 16, 36, 64, 100]复制代码
for 循环能够嵌套,所以,在列表生成式中,也能够用多层 for 循环来生成列表。
[m + n for m in 'ABC' for n in '123']
# ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']复制代码
JS 中 ES6 的 默认参数正是借鉴于 Python,用法以下:
def greet(name='World'):
print 'Hello, ' + name + '.'
greet() # Hello, World.
greet('Python') # Hello, Python.复制代码
相似于 JS 函数中自动识别传入参数的个数,Python 也提供了定义可变参数,即在可变参数的名字前面带上个 *
号。
def fn(*args):
print args
fn() # ()
fn('a') # ('a',)
fn('a', 'b') # ('a', 'b')复制代码
Python 解释器会把传入的一组参数组装成一个 tuple 传递给可变参数,所以,在函数内部,直接把变量 args 当作一个 tuple 就行了。
Python 中经常使用的函数 (map、reduce、filter) 的做用和 JS 中一致,只是用法稍微不一样。
def f(x):
return x * x
print map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) # [1, 4, 9, 16, 25, 36, 49, 64, 81]复制代码
def f(x, y):
return x * y
reduce(f, [1, 3, 5]) # 15复制代码
def is_odd(x):
return x % 2 == 1
filter(is_odd, [1, 4, 6, 7, 9, 12, 17]) # [1, 7, 9, 17]复制代码
和 JS 的匿名函数不一样的地方是,Python 的匿名函数中只能有一个表达式,且不能写 return。拿 map() 函数为例:
map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]) # [1, 4, 9, 16, 25, 36, 49, 64, 81]复制代码
关键词 lambda 表示匿名函数,冒号前面的 x 表示函数参数,能够看出匿名函数 lambda x: x* x
实际上就是:
def f(x):
return x * x复制代码
以前写过一些关于 JS 闭包的文章,好比 深刻浅出JavaScript之闭包(Closure)、以及 读书笔记-你不知道的 JavaScript (上),Python 中闭包的定义和 JS 中的是一致的即:内层函数引用了外层函数的变量,而后返回内层函数。下面来看下 Py 中闭包之 for 循环经典问题:
# 但愿一次返回3个函数,分别计算1x1,2x2,3x3:
def count():
fs = []
for i in range(1, 4):
def f():
return i * i
fs.append(f)
return fs
f1, f2, f3 = count() # 这种写法至关于 ES6 中的解构赋值
print f1(), f2(), f3() # 9 9 9复制代码
老问题了,f1(), f2(), f3() 结果不该该是 1, 4, 9 吗,实际结果为何都是 9 呢?
缘由就是当 count() 函数返回了 3 个函数时,这 3 个函数所引用的变量 i 的值已经变成了 3。因为 f一、f二、f3 并无被调用,因此,此时他们并未计算 i*i,当 f1 被调用时,i 已经变为 3 了。
要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。代码修改以下:
方法一: 能够理解为建立了一个封闭的做用域,i 的 值传给 j 以后,就和 i 没任何关系了。每次循环造成的闭包都存进了内存中。
def count():
fs = []
for i in range(1, 4):
def f(j):
def g(): # 方法一
return j * j
return g
r = f(i)
fs.append(r)
return fs
f1, f2, f3 = count()
print f1(), f2(), f3() # 1 4 9复制代码
方法二:思路比较巧妙,用到了默认参数 j 在函数定义时能够获取到 i 的值,虽然没有用到闭包,可是和方法一有殊途同归之处。
def count():
fs = []
for i in range(1, 4):
def f(j = i): # 方法二
return j * j
fs.append(f)
return fs
f1, f2, f3 = count()
print f1(), f2(), f3() # 1 4 9复制代码
ES6 的语法中的 decorator 正是借鉴了 Python 的 decorator。decorator 本质上就是一个高阶函数,它接收一个函数做为参数,而后返回一个新函数
。
那装饰器的做用在哪呢?先上一段平常项目中用 ts 写的网关代码:
@Post('/rider/detail') // URL 路由
@log() // 打印日志
@ResponseBody
public async getRiderBasicInfo(
@RequestBody('riderId') riderId: number,
@RequestBody('cityId') cityId: number,
) {
const result = await this.riderManager.findDetail(cityId, riderId)
return result
}复制代码
能够看出使用装饰器能够极大地简化代码,避免每一个函数(好比日志、路由、性能检测)编写重复性代码。
回到 Python 上,Python 提供的 @ 语法来使用 decorator,@ 等价于 f = decorate(f)
。下面来看看 @log() 在 Python 中的实现:
# 咱们想把调用的函数名字给打印出来
@log()
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10)
# 来看看 @log() 的定义
def log():
def log_decorator(f):
def fn(x):
print '调用了函数' + f.__name__ + '()'
return f(x)
return fn
return log_decorator
# 结果
# 调用了函数 factorial()
# 3628800复制代码
面向对象编程是一种程序设计范式,基本思想是:用类定义抽象类型,而后根据类的定义建立出实例。在掌握其它语言的基础上,仍是比较容易理解这块知识点的,好比从下面两种写法能够看出不一样语言的语言特性间居然有如此多的共性。
es6: (附:本文的主题是 python,因此只是初略展现下 js 中类的定义以及实例的建立,为了说明写法的类似性)
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
}
const child1 = new Person('Xiao Ming', 10)复制代码
Python: (核心要点写在注释中)
# 定义一个 Person 类:根据 Person 类就能够形成不少 child 实例
class Person(object):
address = 'Earth' # 类属性 (实例公有)
def __init__(self, name, age): # 建立实例时,__init__()方法被自动调用
self.name = name
self.age = age
def get_age(self): # 定义实例方法,它的第一个参数永远是 self,指向调用该方法的实例自己,其余参数和普通函数是同样的
return self.age
child1 = Person('Xiao Ming', 10)
child2 = Person('Xiao Hong', 9)
print child1.name # 'Xiao Ming'
print child2.get_age() # 9
print child1.address # 'Earth'
print child2.address # 'Earth'复制代码
child 属于 Student 类,Student 类属于 People 类,这就引出了继承: 即得到了父类的方法属性后又能添加本身的方法属性。
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
class Student(Person):
def __init__(self, name, age, grade):
super(Student, self).__init__(name, age) # 这里也能这样写 Person.__init__(self, name, age)
self.grade = grade
s = Student('Xiao Ming', 10, 90)
print s.name # 'Xiao Ming'
print s.grade # 90复制代码
能够看到子类在父类的基础上又增长了 grade 属性。咱们能够再来看看 s 的类型。
isinstance(s, Person)
isinstance(s, Student)复制代码
能够看出,Python 中在一条继承链上,一个实例能够当作它自己的类型,也能够当作它父类的类型。