Lang-Python

个人笔记,方便自己查阅使用

Py.LangSpec.Contents

  • Refs
  • Built-in
  • Closure
  • Collections
  • Data Structures
    • Dictionary
      • Comprehension
      • Sort
      • Traverse
    • List
      • List Comprehensions
    • Sets
  • Data Structures (Implementations)
  • Decorators
  • Exception
  • Functions
    • Parameters& Arguments
  • Generators, yield, Iterables
  • Grammar/Syntax
  • I/O
  • Iterable
  • Lambda, filter, reduce and map
  • Logic
  • Object and Class
    • metaclass
    • class variables
  • Object Types
    • 检查空对象?
    • is vs ==
    • None
  • Operators
  • Scope
  • Strings
  • Style
  • Variables, Objects, Types, References
    • python中的变量 和 引用
      • 函数传参
    • 强类型,弱类型?
    • Global vars
  • With..as..
  • Topics
    • Commandline
    • Concurrency
      • GIL
    • Debug
    • Encoding
    • Errors
      • Errors you meet
    • File
    • Garbage Collection
    • Sort
    • Regular Expression
  • System&Software
  • Questions/Problems

Refs

Lang Py 2.7

Built-in

  • compile
    • The compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1) built-in can be used to speed up repeated invocations of the same code with exec or eval by compiling the source into a code object beforehand.
    • Doc: Compile
    • Compile the source into a code or AST object. Code objects can be executed by an exec statement or evaluated by a call to eval().
  • eval, exec
    • What's the difference between eval, exec, and compile in Python?
    • The exec function (which was a statement in Python 2) is used for executing a dynamically created statement or program
    • only exec accepts source code that contains statements like def, for, while, import, or class
    • Both exec and eval accept 2 additional positional arguments - globals and locals - which are the global and local variable scopes that the code sees.
    • compile?

Closure

Python’s closures are late binding. This means that the values of variables used in closures are looked up at the time the inner function is called. (RUNTIME)

def multipliers():
    return [lambda x : i * x for i in range(4)]

print [m(2) for m in multipliers()]

The output of the above code will be [6, 6, 6, 6] (not [0, 2, 4, 6]).

The reason for this is that Python’s closures are late binding. This means that the values of variables used in closures are looked up at the time the inner function is called. So as a result, when any of the functions returned by multipliers() are called, the value of i is looked up in the surrounding scope at that time. By then, regardless of which of the returned functions is called, the for loop has completed and i is left with its final value of 3. Therefore, every returned function multiplies the value it is passed by 3, so since a value of 2 is passed in the above code, they all return a value of 6 (i.e., 3 x 2).

对于这个例子,重要的一个点是,for i in range(4)是不属于这个lambda函数的,所以multipliers返回的是一个含有4个function的列表。late binding告诉我们,运行该lambda函数的时候,我们才计算函数的内容(也不一定要是lambda,一般的函数也可以,因为我们这里是讨论闭包),而彼时i已经变成3。这个问题的难度在于,把列表推导式使用的变量与lambda函数调用的变量用到了一起,再加上了闭包。

Collections

OrderedDict

有序的字典对象

  • Doc collections.OrderedDict
    • OrderedDict.popitem(last=True)
    • The popitem() method for ordered dictionaries returns and removes a (key, value) pair. The pairs are returned in LIFO order if last is true or FIFO order if false.

Data Structures (Types)

  • Sequential Data Types or: Sequence

  • del 用del来删除dict/list中的一个/多个元素,比使用remove要快

Dictionary

List

  • Initialization 初始化
    • plist = ['a',1,obj1]
    • plist = ["wulala"] 得到 ['wulala']
    • plist = list() 空列表 或者 plist=[]
    • NOTE: plist = list["wula"]
      • 会得到 ['w','u','l','a'] !!!
  • 切片
    • python 列表切片
      • print li[::-1] #输出[7,6,5,4,3,2,1],省略起始索引、终止索引,步长值为-1,表示反向获取
    • 倒序输出列表
      • range()返回列表
      • range(0,3)[::-1] #输出[2,1,0]
  • the statement list = [ [ ] ] * 5 does NOT create a list containing 5 distinct lists; rather, it creates a a list of 5 references to the same list (i.e. [] here)
    • list[0].append(10) appends 10 to the first list. But since all 5 lists refer to the same list, the output is: [[10], [10], [10], [10], [10]].
    • Similarly, list[1].append(20) appends 20 to the second list. But again, since all 5 lists refer to the same list, the output is now: [[10, 20], [10, 20], [10, 20], [10, 20], [10, 20]].
    • In contrast, list.append(30) is appending an entirely new element to the “outer” list, which therefore yields the output: [[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30].

List Comprehensions

  • Generator Comprehension, Set Comprehension, etc
  • List 推导式 深入理解
  • list comprehension中,只能使用for和if这2种语句。
    • a = 5 if b > 3 else 2 (python 中没有?:)
  • Q:Python的List Comprehension中,可以使用无限多个for、if语句,该怎么去理解这些for、if语句呢?它们之间的关系是什么呢?
  • A: Python的语法解析、字节码生成,大约分为3个阶段:
    1、将.py源代码,解析成语法树
    2、将语法树,解析成AST树
    3、根据AST树,生成字节码
    • Python在从语法树生成AST的过程中,会将List Comprehension中的for分离开来,并将每个if语句,全部归属于离他最近的左边的for语句
    • 如果变量x没有被使用过,那么变量x会成为一个局部变量,当列表推导式结束后,还可以访问变量x;否则,变量x原来的作用域是什么,现在还是什么。
    • 后续的for语句都嵌套在之前的for语句中

List Comprehension, python-course

Set Comprehension: use curly brackets instead of square brackets to create a set.

no_primes = {j for i in range(2,sqrt_n) for j in range(i*2, n, i)}

Python: List Comprehensions

S = [x**2 for x in range(10)]
V = [2**i for i in range(13)]
M = [x for x in S if x % 2 == 0] ## [0, 4, 16, 36, 64]
m = [x for x in [x**2 for x in range(10)] if x%2==0] ## [0, 4, 16, 36, 64]

>>> noprimes = [j for i in range(2, 8) for j in range(i*2, 50, i)] # 倘若写成 noprimes = [j for j in range(i*2, 50, i) for i in range(2, 8) ] 就不是本来的意思了,因为后续的for语句都嵌套在之前的for语句中
primes = [x for x in range(2, 50) if x not in noprimes]
>>> print primes
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47] 

[(x,y,z) for x in range(1,30) for y in range(x,30) for z in range(y,30) if x**2 + y**2 == z**2] 

you can nest list comprehensions inside of each other, so you could write the above example with a single statement (without the need for the temporary variable "noprimes").

Set

Data Structures (Implementations)

Trees

Decorators

Exception

  • python 异常处理
    • try...except[SomeError]; try...except; raise触发异常
    • e.g. raise ShortInputException(len(s), 3)
      • 处理:except ShortInputException, xs:

Functions

  • python里函数定义的顺序
    • 函数里的语句在函数定义时并不执行,只有在该function being called的时候才执行
  • Zip(): 强大的zip
    • zip([seql, ...])接受一系列可迭代对象作为参数,将对象中对应的元素打包成一个个tuple(元组),然后返回由这些tuples组成的list(列表)。若传入参数的长度不等,则返回list的长度和参数中长度最短的对象相同。
    • zip()配合*号操作符,可以将已经zip过的列表对象解压

Parameters & Arguments

  • Python的函数参数传递:传值?引用?
    在python中,类型属于对象,变量是没有类型的.
    • 所有的变量都可以理解是内存中一个对象的“引用”,或者,也可以看似c中void*的感觉。所以,希望大家在看到一个python变量的时候,把变量和真正的内存对象分开。
    • “可更改”(mutable)与“不可更改”(immutable)对象:在python中,strings, tuples, 和numbers是不可更改的对象,而list,dict等则是可以修改的对象。
  • python中的传值和传引用
    • 传递参数的时候,python不允许程序员选择采用传值还是传引用。Python参数传递采用的肯定是“传对象引用”的方式。实际上,这种方式相当于传值和传引用的一种综合。
    • 如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值--相当于通过“传引用”来传递对象。
    • 如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修改原始对象--相当于通过“传值'来传递对象。
  • 函数参数的多种传递方法
    • * 包裹传递: 所有的参数被收集,根据位置合并成一个元组(tuple)
    • ** 包裹关键字传递:所有的参数被收集,根据位置和关键字合并成一个字典(dict)
    • 解包裹传递: 就是在传递tuple时,让tuple的每一个元素对应一个位置参数; dict的解包裹类似
  • 默认参数:
    • 如果默认参数是一个mutable,而多次调用此function时,使用了该默认参数,会怎样呢?
    • See question 1 8 Essential Python Interview Questions
    • expressions in default arguments are calculated when the function is defined, not when it’s called.

Generators, yield, Iterables

  • DOC generators

  • Generators functions allow you to declare a function that behaves like an iterator, i.e. it can be used in a for loop.

  • Python关键字yield的解释(stackoverflow)

  • To understand what yield does, you must understand what generators are. And before generators come iterables.

  • Generators are iterators, but you can only iterate over them once(第二次不会报错,但什么也不会发生). It's because they do not store all the values in memory, they generate the values on the fly

  • Yield is a keyword that is used like return, except the function will return a generator.

    • IMPORTANT: when you call the function (with yield), the code you have written in the function body does not run. The function only returns the generator object
  • extend() 是一个迭代器方法,作用于迭代器,并把参数追加到迭代器的后面

  • Python yield 用法

    • yield 简单说来就是一个生成器,生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。
  • GREAT TUT: IBM Python yield 使用浅析

    • 考虑可复用性,并且节省内存占用
    • 带有 yield 的函数在 Python 中被称之为 generator(生成器)

Generator Comprehension

Generator comprehensions were introduced with Python 2.6. They are simply a generator expression with a parenthesis - round brackets - around it. Otherwise, the syntax and the way of working is like list comprehension, but a generator comprehension returns a generator instead of a list

Grammar/Syntax

Late Binding

I/O

  • Python Programming/Input and Output
    • input() uses raw_input to read a string of data, and then attempts to evaluate it as if it were a Python program, and then returns the value that results.
  • File Objects
    • File objects are implemented using C’s stdio package and can be created with the built-in open() function.
    • It is an iterator
      • it can only traverse the file once.
      • You may reset the file cursor with .seek(0)
    • Actually, C's stdio is also streaming

Print

想要print始终显示在同一行,本身是在最后加上逗号即可,即:
print "xxx",

然后又想要实现,新打印的一行,冲掉之前旧的一行,达到显示出下载文件大小一点点增加,但是却始终保持同行,那么就再打印的内容最后添加上\r即可:
print "xxx\r",

Stream - io module

  • io — Core tools for working with streams¶
  • The io module provides the Python interfaces to stream handling. Under Python 2.x, this is proposed as an alternative to the built-in file object, but in Python 3.x it is the default interface to access files and streams.

Iterable

  • Python's iterator, iterable, and iteration protocols?
    • An iterable is an object that has an __iter__ method which returns an iterator, or which defines a __getitem__ method that can take sequential indexes starting from zero (and raises an IndexError when the indexes are no longer valid). So an iterable is an object that you can get an iterator from.
    • An iterator is an object with a next (Python 2) or __next__ (Python 3) method.

Whenever you use a for loop, or map, or a list comprehension, etc. in Python, the next method is called automatically to get each item from the iterator, thus going through the process of iteration.

Lambda, filter, reduce and map

  • Lambda, filter, reduce and map

  • Lambda functions are mainly used in combination with the functions filter(), map() and reduce(). The lambda feature was added to Python due to the demand from Lisp programmers.

  • Lambda 表达式有何用处?如何使用?

    • 只能由一条表达式组成
    • 一般可以在sorted, max, 这类函数里的key用lambda.比如有一个比较复杂的数组结构,s = [('a', 3), ('b', 2), ('c', 1)]
      • 对这个数组用第二个元素排序。可以写成 sorted(s, key=lambda x:x[1])

map

  • r = map(func, seq)
  • r=map(lambda x: x+"abc","gre") # r为['gabc', 'rabc', 'eabc']
  • map() can be applied to more than one list (or sequence). The lists have to have the same length and the function or lambda should be able to take more than one args.

filter

  • filter(function, list) offers an elegant way to filter out all the elements of a list, for which the function function returns True.

reduce

  • reduce(func, seq) continually applies the function func() to the sequence seq. It returns a single value.
  • it goes: [ func(func(s1, s2),s3), ... , sn ]
  • reduce(lambda x,y: x+y, [47,11,42,13]) ==>113
    • f = lambda a,b: a if (a > b) else b
    • reduce(f, [47,11,42,102,13])
    • ==> 102

Logic

if not:

if条件语句后面需要跟随bool类型的数据,即True或者False。然而,如果不是bool类型的数据,可以(自动)将其转换成bool类型的数据,转换的过程是隐式的
在Python中,None、空列表[]、空字典{}、空元组()、0等一系列代表空和无的对象会被转换成False。除此之外的其它对象都会被转化成True。
在命令if not 1中,1便会转换为bool类型的True。not是逻辑运算符非,not 1则恒为False。因此if语句if not 1之下的语句,永远不会执行。

  • there is no else if in Python, only elif

Object and Class

Metaclass

  • Stackoverflow e-satis metaclass
  • 译文: 深刻理解Python中的元类(metaclass)
  • Classes are objects too
    • This object (the class) is itself capable of creating objects (the instances), and this is why it's a class.
    • you can assign it to a variable
    • you can copy it
    • you can add attributes to it
    • you can pass it as a function parameter
  • Creating classes dynamically: like creating any object
    • e.g: in a function
  • type方法能动态的创建类: 接受一个类的描述作为参数,然后返回一个类
    • 为你的类增加方法? define a function with the proper signature and assign it as an attribute=>放入type的dictionary 参数中

Class variables

  • in Python, class variables are internally handled as dictionaries. If a variable name is not found in the dictionary of the current class, the class hierarchy (i.e., its parent classes) are searched until the referenced variable name is found (if the referenced variable name is not found in the class itself or anywhere in its hierarchy, an AttributeError occurs).
  • if any of its child classes overrides that value, then the value is changed in that child only.
  • if the value is then changed in the Parent, that change is reflected also by any children that have not yet overridden the value
  • see q3 8 interview qs

Class and its object

Copy

  • doc: copy — Shallow and deep copy operations
    • import copy
    • copy.copy(x)
      • Return a shallow copy of x.
    • copy.deepcopy(x)
      • Return a deep copy of x.
  • python中的深拷贝和浅拷贝理解
    • 利用切片操作和工厂方法list方法拷贝就叫浅拷贝,只是拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已。 id 一样
    • 利用copy中的deepcopy方法进行拷贝就叫做深拷贝,外围和内部元素都进行了拷贝对象本身,而不是引用。 id 不同
  • Python 拷贝对象(深拷贝deepcopy与浅拷贝copy)
    • Python中的对象之间赋值时是按引用传递的,如果需要拷贝对象,需要使用标准库中的copy模块。
      1. copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象。
      1. copy.deepcopy 深拷贝 拷贝对象及其子对象

Instantiation 实例化

  • woodpecker 5.4. 类的实例化
  • Python 对象的实例化过程分析 init and new
    • Python 的构造函数有两个,一个是我们最常用的 init ,另一个是很少用到的 new 。而从它们的函数定义 def new(cls, [...]) 和 def init(self, [...]) 可以知道, init 被调用时实例已经被创建(就是 self 参数所引用的);而 new 被调用的时候,实例不一定已被创建(暂时只能这么说),而 new 的第一个参数为当前类的引用。

Other

举例:
    # Definition for a binary tree node.
    # class TreeNode(object):
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None

    t = root.left
    root.left = root.right 
    root.right = t

相当于:

    root.left, root.right = root.right, root.left

Object Types

Assignment:

  • python赋值和拷贝----一切皆对象,参数皆引用
    • 所谓“传值”也就是赋值的意思.
    • Python不允许程序员选择采用传值还是传引用。Python参数传递采用的肯定是“传对象引用”的方式。实际上,这种方式相当于传值和传引用的一种综合。如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值——相当于通过“传引用”来传递对象。如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修改原始对象——相当于通过“传值”来传递对象。
    • 当人们复制列表或字典时,就复制了对象列表的引用,如果改变引用的值,则修改了原始的参数
  • zhihu

Basics

检查类型:

空对象? 检查空对象?

  • 要返回空?
    • return: 返回None,不是False也不是True
    • return []: [],bool值为False

is vs ==

You use == when comparing values and is when comparing identities.

  • Python: "is None" vs "==None"
    • "Comparisons to singletons like None should always be done with 'is' or 'is not', never the equality operators."
    • "is" is a quick id comparison, whereas "==" requires a dict lookup on both the "1" and "None" objects for the comparison operators

None

Python中的None

  • None是一个特殊的常量。
  • None is actually a null object!
  • None和False不同。但None的布尔值是False
    +** False的有None, 0, []**
  • None不是0。
    None不是空字符串。
    None和任何其他的数据类型比较永远返回False。
    None有自己的数据类型NoneType。
    你可以将None复制给任何变量,但是你不能创建其他NoneType对象。

number

整数取整:小数点后的都不要(所以是向0取整)(经过测试)

Operators

python运算符优先级

Division

Python 2 automatically performs integer arithmetic if both operands are integers. As a result, 5/2 yields 2, while 5./2 yields 2.5.

the “double-slash” (//) operator will always perform integer division, regardless of the operand types. That’s why 5.0//2.0 yields 2.0 even in Python 2.

Python 2中:

x = 4.5
y = 2
print x//y  结果是2.0
x = 4.5
y = 2
print x/y  结果是2.25
5//2 结果 2
5/2  结果 2

Python3中除法“/”变成了浮点除法,不默认整数除法,而//则保留仍然进行整数除法

Scope 作用域

  • Python的作用域 探讨: global name is not defined
    • 在Python中,只有模块,类以及函数才会引入新的作用域,其它的代码块是不会引入新的作用域的。
    • 在Python中,使用一个变量之前不必预先声明它,但是在真正使用它之前,它必须已经绑定到某个对象;而名字绑定将在当前作用域中引入新的变量,同时屏蔽外层作用域中的同名变量,不论这个名字绑定发生在当前作用域中的哪个位置。

Strings

Style

  • Python 续行符
    • 当一条命令用续行符 (“\”) 分割成多行时, 后续的行可以以任何方式缩近, 此时 Python 通常的严格的缩近规则无需遵守。
    • 在小括号, 方括号或大括号中的表达式 (如 定义一个 dictionary) 可以用或者不用续行符 (“\”) 分割成多行。

Variables, Objects, Types, References

python中的变量 和 引用

变量存储在内存中的值。这就意味着在创建变量时会在内存中开辟一个空间。
基于变量的数据类型,解释器会分配指定内存,并决定什么数据可以被存储在内存中。
因此,变量可以指定不同的数据类型,这些变量可以存储整数,小数或字符。

变量在内存中存储时,包含的信息:id,名称,数据,数据类型(由解释器决定)

Python的所有变量其实都是指向内存中的对象的一个指针/引用,所有的变量都是。此外,对象还分两类:一类是可修改的,一类是不可修改的

函数传参

调用函数时,把参数里传入的东西对相应对象的引用依次赋给对应的内部变量。而此时,则要考虑到,外部namespace和函数内部namespace的隔离。

全局变量和本地变量

如果一个函数里面使用了一个变量,那么 Python 会先看看有没有对应的本地变量,如果没有找到,但找到一个全局变量,那么 Python 会把那个全局变量的引用赋给一个新的本地变量。所以,现在在函数里的那个变量和全局变量其实不是同一个变量,他们只不过暂时有了相同的引用。这样其实可以看作 Python 为你做了隐式的参数传递。 ==>> 局部里修改一个全局变量,全局变量会改变

强类型,弱类型?

  • Python 是强类型语言?
    • 是:"1"+2 you will get traceback error
    • 强、弱类型
      • 强类型strongly typed: 如果一种语言的所有程序都是well behaved——即不可能出现forbidden behaviors,则该语言为strongly typed。
        弱类型weakly typed: 否则为weakly typed。比如C语言的缓冲区溢出,属于trapped errors,即属于forbidden behaviors..故C是弱类型
    • 弱类型语言,类型检查更不严格,如偏向于容忍隐式类型转换。譬如说C语言的int可以变成double。 这样的结果是:容易产生forbidden behaviours,所以是弱类型的
    • 动态、静态类型
      • 静态类型 statically: 如果在编译时拒绝ill behaved程序,则是statically typed;
        动态类型dynamiclly: 如果在运行时拒绝ill behaviors, 则是dynamiclly typed。
        静态类型可以分为两种:
        如果类型是语言语法的一部分,在是explicitly typed显式类型;
        如果类型通过编译时推导,是implicity typed隐式类型, 比如ML和Haskell
    • 例子:
      • 无类型: 汇编
        弱类型、静态类型 : C/C++
        弱类型、动态类型检查: Perl/PHP
        强类型、静态类型检查 :Java/C#
        强类型、动态类型检查 :Python, Scheme
        静态显式类型 :Java/C
        静态隐式类型 :Ocaml, Haskell

Global vars

  • 浅析Python中的Python全局变量
  • 应该尽量避免使用Python全局变量。不同的模块都可以自由的访问全局变量,可能会导致全局变量的不可预知性。

With..as..


Topics

Commandline

Run programs from commandline

  • learn py the hard way Exercise 50: Your First Website
  • run python bin/app.py would go, but cd bin/, python app.py would go wrong.
    • In all Python projects you do not cd into a lower directory to run things. You stay at the top and run everything from there so that all of the system can access all the modules and files.

Concurrency

对于任何Python程序,不管有多少的处理器,任何时候都总是只有一个线程在执行。 "不要使用多线程,请使用多进程。"GIL对诸如当前线程状态和为垃圾回收而用的堆分配对象这样的东西的访问提供着保护。然而,这对Python语言来说没什么特殊的,它需要使用一个GIL。这是该实现的一种典型产物。现在也有其它的Python解释器(和编译器)并不使用GIL。虽然,对于CPython来说,自其出现以来已经有很多不使用GIL的解释器。

由于GIL,python的多线程,threading非常适合i/o密集,而不适合cpu密集。如果要利用多核,则要使用Multiprocessing

multiprocessing pool & dummy

  • multiprocessing.dummy 是 multiprocessing 模块的完整克隆,唯一的不同在于 multiprocessing 作用于进程,而 dummy 模块作用于线程; 可以针对 IO 密集型任务和 CPU 密集型任务来选择不同的库. IO 密集型任务选择multiprocessing.dummy,CPU 密集型任务选择multiprocessing.
  • multiprocessing.dummy 模块与 multiprocessing 模块的区别: dummy 模块是多线程,而 multiprocessing 是多进程, api 都是通用的。 所有可以很方便将代码在多线程和多进程之间切换
  • multiprocessing.dummy replicates the API of multiprocessing but is no more than a wrapper around the threading module.
  • multiprocessing.pool和dummy都可以使用map来简化多线程
  • 实例:python-multi-threading使用multiprocessing.dummy
  • python 进程池1 - Pool使用简介

GIL

  • python 线程,GIL 和 ctypes
  • Global Interpreter Lock ,意即全局解释器锁
    • Python 语言的主流实现 CPython 中,GIL 是一个货真价实的全局线程锁,在解释器解释执行任何 Python 代码时,都需要先获得这把锁才行,在遇到 I/O 操作时会释放这把锁。如果是纯计算的程序,没有 I/O 操作,解释器会每隔 100 次操作就释放这把锁,让别的线程有机会执行(这个次数可以通过 sys.setcheckinterval 来调整)。
    • 所以虽然 CPython 的线程库直接封装操作系统的原生线程,但 CPython 进程做为一个整体,同一时间只会有一个获得了 GIL 的线程在跑,其它的线程都处于等待状态等着 GIL 的释放。
    • 这也就解释了之前的实验结果:虽然有两个死循环的线程,而且有两个物理 CPU 内核,但因为 GIL 的限制,两个线程只是做着分时切换,总的 CPU 占用率还略低于 50%。
  • 历史原因:多核 CPU 在 1990 年代还属于类科幻,Guido van Rossum 在创造 python 的时候,也想不到他的语言有一天会被用到很可能 1000+ 个核的 CPU 上面,一个全局锁搞定多线程安全在那个时代应该是最简单经济的设计了。简单而又能满足需求,那就是合适的设计
  • 解决
    • Python 在 2.6 里新引入了 multiprocessing 这个多进程标准库,让多进程的 python 程序编写简化到类似多线程的程度,大大减轻了 GIL 带来的不能利用多核的尴尬。
    • C拓展:如果不想用多进程这样重量级的解决方案,还有个更彻底的方案,放弃 Python,改用 C/C++。当然,你也不用做的这么绝,只需要把关键部分用 C/C++ 写成 Python 扩展,其它部分还是用 Python 来写,让 Python 的归 Python,C 的归 C。一般计算密集性的程序都会用 C 代码编写并通过扩展的方式集成到 Python 脚本里(如 NumPy 模块)。在扩展里就完全可以用 C 创建原生线程,而且不用锁 GIL,充分利用 CPU 的计算资源了。不过,写 Python 扩展总是让人觉得很复杂。
    • ctypes: Python 还有另一种与 C 模块进行互通的机制 : ctypes
      • 利用 ctypes 绕过 GIL: ctypes 与 Python 扩展不同,它可以让 Python 直接调用任意的 C 动态库的导出函数。
      • Python中开线程,用C跑
  • GIL WIKI
  • Understanding GIL
    • Python 3.2 -> New GIL
    • 在新的GIL实现中,用一个固定的超时时间来指示当前的线程放弃全局锁。在当前线程保持这个锁,且其他线程请求这个锁的时候,当前线程就会在5ms后被强制释放掉这个锁。

Debug

Encoding

理解unicode, utf-8

  • unicode是字符集的映射(编码的映射),utf-8等等是编码方式(在计算机中怎么存储)。 gbk,ascii也是编码,不过不是对应unicode字符集。
    • unicode用数字0-0x10FFFF来映射字符
    • UTF-8、UTF-16、UTF-32都是将数字转换到程序数据的编码方案。一个unicode编码按16进制很明显会超过一个字节,所以如何按字节存储于计算机中,就是编码方案
    • 通用字符集(Universal Character Set, UCS)是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义的标准字符集。UCS-2用两个字节编码,UCS-4用4个字节编码。
    • \u7edf\u4e00\u7801("统一码")便是一个unicode编码序列

Python's encoding problems

Source code encoding

  • PEP 0263 -- Defining Python Source Code Encodings
  • Python will default to ASCII as standard encoding if no otherencoding hints are given.
  • To define a source code encoding, a magic comment must be placed into the source files either as first or second line in the file, such as:
      • # coding=<encoding name>
      • #!/usr/bin/python
      • # -*- coding: <encoding name> -*-
  • More precisely, the first or second line must match the regular expression "coding[:=]\s*([-\w.]+)".
    • \s matches any whitespace character
    • \w matches any alphanumeric character and the underscore
    • \W matches any non-alphanumeric character

Unicode? String?

  • 任何两种字符编码之间如果想完成转化,必须要通过unicode这个桥梁,先把它转化成unicode对象;+ unicode对象直接进行输出,往往会出现乱码,需要解码成str对象。
  • str.encode()实际上就等价于str.decode(sys.defaultencoding).encode();而python的默认编码则是ascii
  • unicode(string [, encoding[, errors]]) -> object
    这个函数的作用是将string按照encoding的格式编码成为unicode对象
  • u"xxx"对应于unicode("xxx", 'utf-8') (如果文件/字符串编码是utf-8)
    • 首先要注意区分,一个字符的不同stage,
      • 在编辑器中输入时是一个stage(或者说在文件/磁盘中存在是一个stage),此时的编码由文件的encoding决定
      • 而在python程序中存在为一个对象时,是第二个stage;
      • 输出(比如在命令行)时,则是第三个stage。
    • u"笔记"在python程序中,将存在为一个unicode对象(type unicode),即 u'\u7b14\u8bb0'
      • print u"笔记"将输出 笔记
    • 而对于string="笔记",则仍是一个string对象,直接输出会显示乱码。正确输出,需要先经过两步,
      • string.decode("utf-8") 假定python程序文件的保存方式是utf-8, 这时候,string仍是一个str type
      • 然后用newstr = unicode(string, 'utf-8')输出,则可以得到正确输出 笔记 . 这时,newstr是一个 unicode type
  • u"xxx"的意思是,xxx是一个Unicode编码序列,所以xxx进入python程序时,就会被作为unicode来处理,因此print之,也是正确的输出。
  • 查看系统的encoding
    • import sys
    • sys.stdout.encoding
    • cp936(microsoft) => GBK

python 字符编码与解码——unicode、str和中文:UnicodeDecodeError: 'ascii' codec can't decode

  • 在python中,编码:unicode-->str;解码str-->unicode.既然是编码,那么就和密码领域一样,编码和解码自然涉及到编码/解码方案(对应加密或者解密算法),unicode相当于明文。在python中,编码函数是encode(),解码函数是decode()。需要注意的一点是,如果我们调用str.encode(),这里涉及到一个隐式的类型转化,会现将str转化成unicode,才能进行编码,这也是不太容易理解的地方。所以,str.encode()实际上就等价于str.decode(sys.defaultencoding).encode().而sys.defaultencoding一般是ascii,它是不能用来编码中文字符的
  • python默认采用ascii编码,而中文编码不在ascii编码能够表示的范围之内,所以string无法将“我的”作为ascii编码保存为str类型。
  • 字符编码/解码函数:
    • unicode:这个是python的内建函数,位于unicode类。
      unicode(string [, encoding[, errors]]) -> object
      这个函数的作用是将string按照encoding的格式编码成为unicode对象
      省略参数将用python默认的ASCII来解码
    • 举例:unicode(someChineseString, "utf-8") 则是将字符按照utf8格式转换成unicode
  • 任何两种字符编码之间如果想完成转化,必须要通过unicode这个桥梁,先把它抓化成unicode对象;unicode对象直接进行输出,往往会出现乱码,需要解码成str对象。
  • 在python中需要使用unicode需要注意:
    1. 程序中出现字符串时一定要加一个前缀u
    2. 不要用str()函数,用Unicode()代替
    3. 不要用过时的string模块。如果传给它非ASCII码,它会把一切搞砸。
    4. 不到必须时不要在你的程序里编解码Unicode字符只在你要写入文件或者数据库或者网络时,才调用encode()函数和decode()函数。
    5. 使用什么字符编码,就要采用对应的字符集进行解码
  • 内建的str()函数和chr()函数不能处理Unicode,它们只能处理常规ASCII编码的字符串,如果一个Unicode字符串作为参数传给了str()函数,它会首先被转换成ASCII码字符串然后交给str()函数

Refs

字符编码简介

需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码(定长码),也是一种前缀码。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。

Errors

Built-in Exceptions

  • ValueError
    • Raised when a built-in operation or function receives an argument that has the right type but an inappropriate value, and the situation is not described by a more precise exception such as IndexError.
    • 例如,str.index('\n')或者str.index('\n')。 如果str中没有'\n',那么raise ValueError

Errors you meet

File

利用迭代协议让for循环自动调用next从而前进到文件的下一行,而不是直接把文件读取到内存中,有三点原因:写法简单,运行速度快,节省内存。示例如下:

for line in open('myfile.py'):
    ...print line.upper()`

而不是使用readlines方法:

for line in open('myfile.py').readlines():
    ...print line.upper()`

The with statement handles opening and closing the file, including if an exception is raised in the inner block. The for line in f treats the file object f as an iterable, which automatically uses buffered IO and memory management so you don't have to worry about large files.

Garbage Collection

  • CPython 使用 mark-sweep & 分代回收
  • Python垃圾回收机制
    • “标记-清除”法是为了解决循环引用问题。可以包含其他对象引用的容器对象(如list, dict, set,甚至class)都可能产生循环引用,为此,在申请内存时,所有容器对象的头部又加上了PyGC_Head来实现“标记-清除”机制。
    • 在为对象申请内存的时候,可以明显看到,实际申请的内存数量已经加上了PyGC_Head的大小 (// objimpl.h, gcmodule.c)

Sort

Sorted

  • python sorted 例子
  • 一般来说,cmp和key可以使用lambda表达式。
  • cmp
    • sorted(L, cmp=lambda x,y:cmp(x[1],y[1]))
  • Key
    • sorted(L, key=lambda x:x[1]))
  • reverse
    • sorted([5, 2, 3, 1, 4], reverse=True)
  • 效率key>cmp(key比cmp快)

Regular Expression

  • \w alphanumerice
    • \W nonalphanumeric
  • Python正则表达式指南
  • tutorialspoint: Python Regular Expressions
  • Functions
    • re.search(pattern, string, flags=0)
      • Scan through string looking for the first location where the regular expression pattern produces a match, and return a corresponding MatchObject instance.
      • 从字符串开头查找,只要子串符合就可以
    • re.match(pattern, string, flags=0)
      • 从字符串开头比较,必须是从头开始的匹配
    • re.split(pattern, string, maxsplit=0, flags=0)
    • re.findall(pattern, string, flags=0)
      • Return all non-overlapping matches of pattern in string, as a list of strings.
    • re.sub(pattern, repl, string, count=0, flags=0)

Regex Lib

  • re.sub('[^0-9a-zA-Z]+', '*', s) Replace all non-alphanumeric characters in a string
  • re.sub('\W+', '', s)

Functions&Utilities

Sort/Sorted

sorted函数是内建函数,他接受一个序列,返回有序的副本。他与sort的唯一区别就是会返回副本 (sorted is not in place!)

问题:如何根据一个字典序列中某一项的值,来对整个序列排序?

Swap?

a,b = b,a 即可

def swap(t1, t2):
    t2, t1 = t1, t2
    return

The code above does not do swap! 全局命名空间和局部命名空间是隔离的,所以:The calling namespace is
not changed,即交换的是局部变量。

def swap(a,b):
    tmp = a 
    a = b 
    b = tmp

The code above does not do swap! 同样是因为局部namespace的隔离。

但是如果在主程序中:

tmp = swap_a
swap_a = swap_b
swap_b = tmp

则swap_a, swap_b 可以交换

Time

  • 测量Python代码运行时间
    • 使用timeit模块
    • 使用time模块
      + Python的标准库手册推荐在任何系统下都尽量使用time.clock()(CPU时间)

System&Software

Interaction with the system

  • 如何调用windows命令
    • import os
    • os.system("dir")

Settings

  • !/usr/bin/python

  • Why do people write #!/usr/bin/env python on the first line of a Python script?
    • If you have several versions of Python installed, /usr/bin/env will ensure the interpreter used is the first one on your environment's $PATH. The alternative would be to hardcode something like #!/usr/bin/python; that's ok, but less flexible.
    • In Unix, an executable file that's meant to be interpreted can indicate what interpreter to use by having a #! at the start of the first line, followed by the interpreter (and any flags it may need).

Questions/Problems

重点:

多态?反射机制?
Iterator&Generator?
数据结构(set,dict, tuple)
Decorators?
内置函数,例如set___, __import等
特殊的语句yield, with
垃圾回收机制
Decorators的定义,以及用法
python线程机制以及为啥python多线程很慢。
Errors and Exceptions

8 Essential Python Interview Questions*

list = ['a', 'b', 'c', 'd', 'e']
print list[10:]

输出[],却不会引起IndexError错误

attempting to access a slice of a list at a starting index that exceeds the number of members in the list will not result in an IndexError and will simply return an empty list.

What makes this a particularly nasty gotcha is that it can lead to bugs that are really hard to track down since no error is raised at runtime.

推荐阅读更多精彩内容