一直在使用 python2.7 版本,这次痛下决心把所有代码移植到 3.x 版本上,从头再复习一遍 python。
前提
以下内容更新到 python 3.12+。
标题下没有内容的部分,其内容和 C,Java 基本一致,就不重复。
教程
模块
文件名 = 模块名 + .py
每个模块都有自己私有的符号表。因此,模块的作者可以在模块内部使用全局变量,而无需担心它与某个用户的全局变量意外冲突。
文件结构与字符编码
#!/usr/local/bin python3
# -*- coding: utf-8 -*-
'This is a module document'
__author__ = "vincen"
from package_name.module_name import what_ever_you_wanted
......
if __name__ == "__main__":
pass
第一行,注释是告诉 Linux/OS X 系统,这是一个 Python 可执行程序,Windows 系统会忽略这个注释。因为 *nix 系统不是按照扩展名执行文件的。
第二行,注释是告诉 Python 解释器,按照 UTF-8 编码读取源代码。
第四行,任何模块代码的第一个字符串都被视为模块的文档注释。
第六行,使用 __author__
变量写入作者名。
第八行,导入其他模块函数的写法。
第十二行,当我们在命令行运行模块文件时,Python 解释器把一个特殊变量 __name__
置为 __main__
,而如果在其他地方导入该模块时,if
判断将失败,因此,这种 if
测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试。
python3.x,默认使用 Unicode 编码。
作用域
书写形式 | 作用域 |
---|---|
__xxx__ |
特殊变量,一般是系统定义的,可以直接引用。如__author__ , __name__ 。获取文档注释可以用__doc__ 变量。 |
_xxx 或__xxx |
这种写法的变量或者函数,是非公开的,不应该被直接引用 |
xxx |
这种写法的变量或者函数,是公开的,可以被直接引用 |
python 没有 public 和 private 关键字,所以公开与非公开只是形式上的,无法强制。
模块的搜索路径
首先搜索当前目录,如果不存在,则搜索 sys.path 变量中定义的目录列表。
>>> print(sys.path)
['','/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python38.zip', '/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8', '/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/lib-dynload', '/Users/vincen/Library/Python/3.8/lib/python/site-packages', '/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages', '/Library/Python/3.8/site-packages']
python 文件编译
Python 会在 __pycache__
目录下以 module.*version*.pyc
名字缓存每个模块编译后的版本。
编译后的模块是跨平台的,所以同一个库可以在不同架构的系统之间共享。
为了减少一个编译模块的大小,你可以在 Python 命令行中使用 -O 或者 -OO。-O 参数删除了断言语句,-OO 参数删除了断言语句和 doc 字符串。
因为某些程序依赖于这些变量的可用性,你应该只在确定无误的场合使用这一选项。“优化的” 模块有一个 .pyo 后缀而不是 .pyc 后缀。未来的版本可能会改变优化的效果。
来自 .pyc
文件或 .pyo
文件中的程序不会比来自 .py
文件的运行更快;.pyc
或 .pyo
文件只是在它们加载的时候更快一些。
包
包的命名习惯和 Java 一样。
模块都存在于包中,而包和普通目录的区别是包含一个名为: __init__.py
的空文件。这样 python 就会认为该目录是一个包。
如果包中的 __init__.py
代码定义了一个名为 __all__
的列表,就会按照列表中给出的模块名进行导入。如:
__all__ = ["echo", "surround", "reverse"]
数据类型
整数
浮点数
字符串
字符串是以单引号 '
或双引号 "
括起来的任意文本。
'''
/"""
三个引号扩起来的文本可以换行。
r'
在引号前加r,是不转义字符串里边的 \
。
布尔值
布尔值包含两种:True, False
布尔值的运算符是:and
, or
, not
。
None
字符
bytes 类型的数据用带 b
前缀的单引号或双引号表示:
x = b'ABC'
表达式
python 一行代码结束不用加 ;
。
变量
变量在程序中就是用一个变量名表示了,变量名必须是大小写英文、数字和 _
的组合,且不能用数字开头。
运算符
>>> 17 // 3 # floor devision discards the fractional part
5
>>> 5 ** 2 # 5 squared
25
del 语句
del 语句。它不同于有返回值的 pop()
方法,它只删除值。语句 del 还可以从列表中删除切片或清空整个列表。
>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
也可以删除整个变量。
>>> del a
输入输出与格式化文本
print() 输出函数
input() 输入函数
在 Python 中,采用的格式化方式和 C 语言是一致的,用 %
实现:
>>> 'Hello, %s' % 'world'
'Hello, world'
>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
'Hi, Michael, you have $1000000.'
有几个 %?
占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个 %?
,括号可以省略。常见的占位符有:
占位符 | 替换内容 |
---|---|
%d | 整数 |
%f | 浮点数 |
%s | 字符串 |
控制流
if
书写形式:if…else… , if…elif…else…
for
书写形式:for…in…
另一种形式:for…in…else… , 只有正常结束的 for 循环(不包含 break 中断的),会执行 else 语句中的内容。
while
break
continue
pass
pass 关键字什么也不做,就是为了不要留空。
>>> while True:
... pass # Busy-wait for keyboard interrupt (Ctrl+C)
>>> class MyEmptyClass:
... pass # This is commonly used for creating minimal classes
>>> def initlog(*args):
... pass # Remember to implement this!
高级数据类型
list
组合使用 appen() 和 pop() 两个方法,可以把列表当作栈使用。
队列一般是 collections.deque
,它为在首尾两端快速插入和删除而设计。
list 的索引位置:
>>> classmates = ['Michael', 'Bob', 'Tracy', 'Vincen']
# 索引 ±0 1 2 3
# 逆序索引 -4 -3 -2 -1
list 里面的元素的数据类型也可以不同,比如:
>>> L = ['Apple', 123, True]
list 元素也可以是另一个 list,比如:
>>> s = ['python', 'java', ['asp', 'php'], 'scheme']
>>> len(s)
4
常用函数
append(), insert(), len(), pop(), sort()
tuple
tuple 和 list 非常类似,但是 tuple 一旦初始化就不能修改。
>>> classmates = ('Michael', 'Bob', 'Tracy')
只有1个元素的 tuple 定义时必须加一个逗号 ,
,来消除歧义:
>>> t = (1,)
>>> t
(1,)
dict
>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
>>> d['Michael']
95
要避免 key 不存在的错误,有两种办法,一是通过 in
判断 key 是否存在:
>>> 'Thomas' in d
False
二是通过 dict 提供的 get()
方法,如果 key 不存在,可以返回 None
,或者自己指定的 value:
>>> d.get('Thomas')
None
>>> d.get('Thomas', -1)
-1
要删除一个 key,用 pop(key)
方法,对应的 value 也会从 dict 中删除:
>>> d.pop('Bob')
75
>>> d
{'Michael': 95, 'Tracy': 85}
常用函数
items(), zip(), sorted(), clear()
set
创建 set 有两种写法:{} 和 set()。可以需要提供一个 list 作为输入集合,重复元素在 set 中自动被过滤。创建空 set 时,必须用 set(),不能用{}。因为后者创造的是空 dict。
>>> s = set([1, 2, 3])
>>> s
{1, 2, 3}
通过 add(key)
方法可以添加元素到 set 中,可以重复添加,但不会有效果:
>>> s.add(4)
>>> s
{1, 2, 3, 4}
通过 remove(key)
方法可以删除元素:
>>> s.remove(4)
>>> s
{1, 2, 3}
set 可以看成数学意义上的无序和无重复元素的集合,因此,两个 set 可以做数学意义上的交集、并集等操作:
>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s1 & s2
{2, 3}
>>> s1 | s2
{1, 2, 3, 4}
函数
python 使用 def 关键字定义函数。
python 的可以返回多个值,但其实是一个 tuple。
python 不允许空函数,所以什么都不做的函数,必须写 pass。
函数参数
如果不能按照默认位置传参,需要加上参数名。
默认参数
def enroll(name, gender, age=6, city='Beijing')
def add_end(L=None)
默认参数必须在普通参数之后。
这是一条最佳实践:默认参数必须指向不变对象!
可变参数
def calc(*numbers)
可变参数在函数内部表现为一个 tuple 类型的值。
关键字参数
def person(name, age, **kw)
关键字参数在函数内部表现为一个 dict 类型的值。
命名关键字参数
命名关键字参数,可以限制关键字参数的名字和数量。
def person(name, age, *, city, job)
命名关键字参数需要一个特殊分隔符 *
,*
后面的参数被视为命名关键字参数。调用方式如下:
>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer
如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符 *
了:
def person(name, age, *args, city, job)
命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错。
命名关键字参数可以有缺省值,从而简化调用。
def person(name, age, *, city='Beijing', job)
参数顺序
参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
参数列表的拆箱
当要传递的参数已经是一个列表,但要调用的函数却接受分开一个个的参数值。这时候要把已有的列表拆开来。如下 list 的分拆和 dict 的分拆:
>>> args = [3, 6]
>>> range(*args)
range(3, 6) # 即[3, 4, 5]
>>> def parrot(voltage, state='a stiff', action='voom'):
... print("voltage = %s, state = %s, action = %s" % (voltage, state, action))
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
lambda表达式
语法限制,lambda 表达式只能有一个单独的表达式。语义上讲,它们只是普通函数定义中的一个语法技巧。lambda 形式可以从外部作用域引用变量。
>>> def make_incrementor(n):
... return lambda x: x + n
>>> f = make_incrementor(42)
>>> f(0)
42
函数注释
python 的函数注释也叫做文档字符串。位于函数定义下一行,以三个引号包含。
格式
第一行应该是关于对象用途的简介。
如果文档字符串有多行,第二行应该空出来,与接下来的详细描述明确分隔。接下来的文档应该有一或多段描述对象的调用约定、边界效应等。
Python 的解释器不会从多行的文档字符串中去除缩进,所以必要的时候应当自己清除缩进。第一行之后的第一个非空行决定了整个文档的缩进格式。
语法糖
Slice
>>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
>>> L[0:5:2] # equals with L[::2]
['Michael', 'Tracy', 'Jack']
如果第一个参数是 0,可以省略;如果第二个参数是 len(L),也可以省略。
第三个参数是跨度,上例表示每隔 1 个元素,取一个值。如果是 1,可以省略。
由于字符串类型是不可变的,所以字符串的切片不能赋值。list 的切片,可以赋值。
List Comprehensions
列表推导式由包含一个表达式的括号组成,表达式后面跟随一个 for 子句,之后可以有零或多个 for 或 if 子句。结果是一个列表,由表达式依据其后面的 for 和 if 子句上下文计算而来的结果构成。
>>> squares = []
>>> for x in range(10):
... squares.append(x**2)
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# lambda 表达式版本
squares = list(map(lambda x: x**2, range(10)))
# 列表推导式版本
squares = [x**2 for x in range(10)]
# 另一个例子
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
其实,推导式,不止用在 list 中,set 中也可以使用推导式,如下:
>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'r', 'd'}
迭代器
凡是可作用于 for
循环的对象都是 Iterable
类型;
凡是可作用于 next()
函数的对象都是 Iterator
类型,它们表示一个惰性计算的序列;
集合数据类型如 list
、dict
、str
等是 Iterable
但不是 Iterator
,不过可以通过 iter()
函数获得一个 Iterator
对象。
Python 的 for
循环本质上就是通过不断调用 next()
函数实现的。
Iterator
是惰性序列,并不会一次性计算出所有内容,放进内存,
面向对象编程
类的格式
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))
if __name__ == "__main__":
stu = Student("vincen", "80")
stu.print_score()
第一行,括号内是继承的父类。所有类的根类是 object。
第二行,__init__
是构造方法,其中第一个参数 self,是所有类的方法的第一个参数。而 self 实际指向创建的实例本身。因此,第三行和第四行,就是把 name 和score变量绑定到Student的实例上。注意:在调用的时候,不用也不能传self的参数。
第十行,创建类的对象时,不需要new关键字。
Getter/Setter
为了避免在类外部,直接访问变量,因此使用get/set方法。如下:
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def get_name(self):
return self.__name
def get_score(self):
return self.__score
def set_name(self, name):
self.__name = name
def set_score(self, score):
self.__score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))
if __name__ == "__main__":
stu = Student("vincen", "80")
stu.print_score()
有些时候,会看到以一个下划线开头的实例变量名,比如 _name
,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然可以被访问,但是,请视为私有变量,不要随意访问”。
双下划线开头的实例变量也不是一定不能从外部访问。不能直接访问 __name
是因为 Python 解释器对外把 __name
变量改成了 _Student__name
,所以,仍然可以通过 _Student__name
来访问 __name
变量:
>>> bart._Student__name
'Bart Simpson'
但是强烈建议你不要这么干,因为不同版本的 Python 解释器可能会把 __name
改成不同的变量名。
总的来说就是,Python 本身没有任何机制阻止你干坏事,一切全靠自觉。
鸭子类型
python 支持鸭子类型,即并不要求严格的继承体系。
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
def run(self):
print('Dog is running...')
class Cat(Animal):
def run(self):
print('Cat is running...')
class Timer(object):
def run(self):
print('Start...')
def run_twice(animal):
animal.run()
animal.run()
在 run_twice(animal)
方法调用时,逻辑上应该传入 Animal 的对象。但是传入 Timer 类型的对象也是可以的。这是因为动态语言,是不需要写明参数类型的,而 Timer 类有 run 方法,符合 Animal 类的结构,因此,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。这就是鸭子类型。
__slots__
class Student(object):
__slots__ = ('name', 'age') # 用tuple 定义允许绑定的属性名称
由于动态语言,可以随意在对象上绑定变量,所以 __slots__
特殊变量可以限制对象能绑定的变量。
函数式编程
python 提供有限的函数式编程支持。
常用函数
map()
map()
函数接收两个参数,一个是函数,一个是 Iterable
,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。
reduce()
reduce
把一个函数作用在一个序列 [x1, x2, x3, ...]
上,这个函数必须接收两个参数,reduce
把结果继续和序列的下一个元素做累积计算。
>>> from functools import reduce
>>> def fn(x, y):
... return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579
filter()
filter()
接收一个函数和一个序列。把传入的函数依次作用于每个元素,然后根据返回值是 True
还是 False
决定保留还是丢弃该元素。
def is_odd(n):
return n % 2 == 1
...
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
装饰器
在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。本质上,decorator 就是一个返回函数的高阶函数。
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2019-05-31')
调用 now()
函数,结果如下:
>>> now()
call now():
2015-3-25
把 @log
放到 now()
函数的定义处,相当于执行了语句:
now = log(now)
由于 log()
是一个 decorator,返回一个函数,所以,原来的 now()
函数仍然存在,只是现在同名的 now
变量指向了新的函数,于是调用 now()
将执行新函数,即在 log()
函数中返回的 wrapper()
函数。
wrapper()
函数的参数定义是 (*args, **kw)
,因此,wrapper()
函数可以接受任意参数的调用。在 wrapper()
函数内,首先打印日志,再紧接着调用原始函数。
如果 decorator 本身需要传入参数,那就需要编写一个返回 decorator 的高阶函数,写出来会更复杂。
import functools
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
@log('execute')
def now():
print('2015-3-25')
执行结果如下:
>>> now()
execute now():
2015-3-25
和两层嵌套的 decorator 相比,三层嵌套的效果是这样的:
>>> now = log('execute')(now)
首先执行 log('execute')
,返回的是 decorator
函数,再调用返回的函数,参数是 now
函数,返回值最终是 wrapper
函数。
以上两个 decorator 中都有 @functools.wraps(func)
的引用。因为函数也是对象,它有 __name__
等属性,但经过 decorator 装饰之后的函数,它们的 __name__
已经从原来的 'now'
变成了 'wrapper'
:
>>> now.__name__
'wrapper'
因为返回的那个 wrapper()
函数名字就是 'wrapper'
,所以,需要把原始函数的 __name__
等属性复制到 wrapper()
函数中,否则,有些依赖函数签名的代码执行就会出错。
不需要编写 wrapper.__name__ = func.__name__
这样的代码,Python 内置的 functools.wraps
就是干这个事的。
偏函数
functools.partial
的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数。
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85
异步
Generator / yeild
generator使用场景
- 当我们需要一个公用的,按需生成的数据。
- 某个事件执行一部分,另一部分在某个事件发生后再执行下一部分,实现异步。
注意事项
- yield from generator_obj 本质上类似于 for item in generator_obj: yield item
- generator函数中允许使用return,但是return 后不允许有返回值
- 生成器和列表推导式的区别在于:生成器存储的是算法,每次调用会计算值。用于生成的数据比较多的情况,不会占用大量内存。
基础
如果一个函数定义中包含 yield
关键字,那么这个函数就不再是一个普通函数,而是一个 generator。generator 有以下特点:
- 遵循迭代器(iterator)协议,迭代器协议需要实现 __iter__、next接口
- 能过多次进入、多次返回,能够暂停函数体中代码的执行
测试代码如下:
def gen_example():
print ('before any yield')
yield 'first yield'
print ('between yields')
yield 'second yield'
print ('no yield anymore')
执行结果如下:
gen = gen_example()
gen.next() # 第一次调用next
#before any yield
#'first yield'
gen.next() # 第二次调用next
#between yields
#'second yield'
gen.next() # 第三次调用next
#no yield anymore
#Traceback (most recent call last):
#File "<stdin>", line 1, in <module>
#StopIteration
调用 gen_example() 方法并没有输出任何内容,说明函数体的代码尚未开始执行。当调用 generator 的 next() 方法,generator 会执行到 yield 表达式处,返回 yield 表达式的内容,然后暂停(挂起)在这个地方,所以第一次调用next打印第一句并返回“first yield”。 暂停意味着方法的局部变量,指针信息,运行环境都保存起来,直到下一次调用 next() 方法恢复。第二次调用 next() 之后就暂停在最后一个 yield,再次调用 next() 方法,则会抛出 StopIteration 异常。
这里,最难理解的就是 generator 和函数的执行流程不一样。函数是顺序执行,遇到 return
语句或者最后一行函数语句就返回。而变成 generator 的函数,在每次调用 next()
的时候执行,遇到 yield
语句返回,再次执行时从上次返回的 yield
语句处继续执行。
# List comprehension
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# Generator 写法一,注意区别只是[]变成了()
>>> g = (x * x for x in range(10))
>>> next(g)
0
>>> next(g)
1
# 没有更多的元素时,抛出 StopIteration 的错误。
# 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'
如果一个函数定义中包含 yield
关键字,那么这个函数就不再是一个普通函数,而是一个 generator:
>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>
举个简单的例子,定义一个 generator,依次返回数字1,3,5:
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)
调用该 generator 时,首先要生成一个 generator 对象,然后用 next()
函数不断获得下一个返回值:
>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
也可以用 for
循环调用 generator,但是拿不到 generator 的 return
语句的返回值。如果想要拿到返回值,必须捕获 StopIteration
错误,返回值包含在 StopIteration
的 value
中。
工程化
Virtual Environment
# set virtual environment
virtualenv --no-site-packages venv
# activate virtual environment
source venv/bin/activate
# deactivate virtual environment
deactivate
# record dependencies
pip3 freeze > requirements.txt
# reload dependencies
pip3 install -r requirements.txt
Development Server
export FLASK_APP=flaskr
export FLASK_ENV=development
flask run
pip3
# upgrade pip3 module
pip3 install --upgrade pip
# 查看所有可更新的模块
pip3 list --outdated
# 从指定的更新源,更新指定的模块
pip3 isntall --upgrade -i https://pypi.douban.com/simple module_name
# 查看当前源
pip3 config list
# 全局设置源,全局设置会写入 ~/.config/pip/pip.config
pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
jupyter-lab
# install
$ pip3 install jupyter-lab
# generate configuration file
$ jupyter-lab --generate-config
Writing default config to: /home/fri/.jupyter/jupyter_lab_config.py
# generate hashed password
$ jupyter-lab password
Wrote hashed password to: /home/fri/.jupyter/jupyter_notebook_config.json
# Create a self-signed SSL certificate
$ mkdir ~/certs
$ cd ~/certs
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout mykey.key -out mycert.pem
# config jupyter_lab_config.py
c.ServerApp.root_dir='/home/fri/jupyter-workspace'
c.ServerApp.allow_remote_access=True
c.ServerApp.ip='*'
c.ServerApp.port=8888
c.PasswordIdentityProvider.password_required=True
c.ServerApp.certfile = '/home/fri/certs/mycert.pem'
c.ServerApp.keyfile = '/home/fri/certs/mykey.key'
# start a server
nohup jupyter-lab --no-browser > jupyter.log 2>&1 &
常用函数
所有内置函数,参考:Built-in Functions
1 ord(), chr()
对于单个字符的编码,Python 提供了 ord()
函数获取字符的整数表示,chr()
函数把编码转换为对应的字符:
>>> ord('A')
65
>>> ord('中')
20013
>>> chr(66)
'B'
>>> chr(25991)
'文'
2 encode(), decode()
以 Unicode 表示的 str
通过 encode()
方法可以编码为指定的 bytes
>>> 'ABC'.encode('ascii')
b'ABC'
>>> '中文'.encode('utf-8')
b'\xe4\xb8\xad\xe6\x96\x87'
如果我们从网络或磁盘上读取了字节流,那么读到的数据就是 bytes
。要把 bytes
变为 str
,就需要用 decode()
方法:
>>> b'ABC'.decode('ascii')
'ABC'
>>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
'中文'
如果 bytes
中只有一小部分无效的字节,可以传入 errors='ignore'
忽略错误的字节:
>>> b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore')
'中'
3 len()
计算 str
包含多少个字符,可以用 len()
函数:
>>> len('ABC')
3
>>> len('中文')
2
如果换成 bytes
,len()
函数就计算字节数:
>>> len(b'ABC')
3
>>> len(b'\xe4\xb8\xad\xe6\x96\x87')
6
>>> len('中文'.encode('utf-8'))
6
4 format()
format() 函数也是格式化字符串的方法,它会用传入的参数依次替换字符串内的占位符 {0}
、{1}
……,不过这种方式写起来比%要麻烦得多:
>>> 'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125)
'Hello, 小明, 成绩提升了 17.1%'
5 range()
range() 函数会产生一个等差数列
range(5)
0, 1, 2, 3, 4
range(5, 10)
5, 6, 7, 8, 9
range(0, 10, 3)
0, 3, 6, 9
6 enumerate()
enumerate 函数可以把一个 list 变成索引-元素对,即获得 list 中每个元素的索引
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
0 A
1 B
2 C
7 dir(), isintance(), type()
如果要获得一个对象的所有属性和方法,可以使用 dir()
函数,它返回一个包含字符串的list
>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']