python 笔记

| 分类 PYTHON  | 标签 python 

一直在使用 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 类型,它们表示一个惰性计算的序列;

集合数据类型如 listdictstr 等是 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() 函数接收两个参数,一个是函数,一个是 Iterablemap 将传入的函数依次作用到序列的每个元素,并把结果作为新的 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 是一个 IteratorIterator 是惰性序列,因此通过 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使用场景

  1. 当我们需要一个公用的,按需生成的数据。
  2. 某个事件执行一部分,另一部分在某个事件发生后再执行下一部分,实现异步。

注意事项

  1. yield from generator_obj 本质上类似于 for item in generator_obj: yield item
  2. generator函数中允许使用return,但是return 后不允许有返回值
  3. 生成器和列表推导式的区别在于:生成器存储的是算法,每次调用会计算值。用于生成的数据比较多的情况,不会占用大量内存。

基础

如果一个函数定义中包含 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 错误,返回值包含在 StopIterationvalue 中。

工程化

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

如果换成 byteslen() 函数就计算字节数:

>>> 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']

上一篇     下一篇