python mechanism

[TOC]

0. is 与 ==的区别

python对象有三个要素:id、type、value。is 比较的是id;==比较的是value
而id实际上是内存地址,(ob1 is ob2) 等价于 (id(ob1) == id(ob2))

另外,一些具体的问题,结果不确定,比如用python.py

1
2
3
4
>>> x = 500
>>> y = 500
>>> x is y
True

然而用python或者IPython,结果为False

  • NOTE: The result of 5 == 5.0 is True! [1]

1. mutable & immutable

不可变(immutable):int、字符串(string)、float、(数值型number)、元组(tuple)

可变(mutable):字典型(dictionary)、列表型(list)

2. with 语句

自动进行对象的生命周期进行管理
Python中的with语句中要求对象实现__enter____exit__函数。调用with语句时,会先分析该语句,执行__enter__函数,然后在当前suite退出时,会调用__exit__函数。__exit__函数中除了可以做释放资源的操作之外,同时也是异常处理的地方。如果当前suite正常退出,没有抛出任何异常,__exit__的几个参数均为None。否则,则将此异常的type、value、traceback作为参数传递给__exit__函数,同时,如果__exit__返回false,此异常会再次抛出,上一级代码suite可以继续处理,如果__exit__返回true,那么此异常就不会被再次抛出了。

  • 同时打开多个文件
    1
    2
    3
    4
    5
    with open(filename1, 'rb') as fp1, open(filename2, 'rb') as fp2, open(filename3, 'rb') as fp3:
    for i in fp1:
    j = fp2.readline()
    k = fp3.readline()
    print(i, j, k)

3. list参数传递

python中的默认变量是定义时得到的,类似于static,其它的时候无论调用几次函数,如果没有传参进来,就会一直用这个默认参数了
正确做法:

1
2
3
4
5
def add(element, mylist=None):
if mylist is None:
mylist = []
mylist.append(element)
return mylist

4*. Python中函数的参数传递与可变长参数

【tricky】可变长度参数:*tupleArg,**dictAr
廖雪峰的更详细的介绍

需要注意python函数参数种类比较多,分类方法杂。下面我试图清晰的分类:

零、 “关键字参数”这个名字,在不同语境下,所指是不同的,及其容易弄混

一、 从实参的角度看

参数只分为位置参数和关键字参数。
所谓关键字参数,就是值和形参名字绑定在一起。
剩下的就是位置参数。
实参传递时,位置参数要在关键字参数之前。
以上四条就是实参传递的所有规则。

1
2
def func(x, y=1):
print(x,y)

二、 从形参的角度看

形参是用来接收实参的值的。
下面按照形参接受实参的顺序排序(注意形参接受实参的顺序不等于形参定义时应该遵从的顺讯)。
首先,最先接收的是实参的关键字参数(也就是实参中指定了形参参数名)。
然后,接收的是实参的位置参数。接收方法就是依次绑定。
第三,默认(形参)参数,也是接收实参的位置参数,只不过对应的实参如果不存在,就用默认参数。正因为默认参数所对应的实参可有可无,所以它在形参中,应该放在位置参数之后。另外,就是著名的python坑,“默认参数一定要指向不变对象”。
PS:其机理是,默认参数是在函数声明时,将默认值的地址绑定到函数的默认参数。因而,如果默认参数是一个可变对象,那么这个可变参数的地址就永远绑定到函数的默认参数上了。之后,每次调用函数,不会改变可变参数的地址,只会改变可变参数的内容。
第四,剩下的实参被可变长度参数(或称可变参数)捕获为tuple。
注意,在未被捕获的实参中,默认参数会先捕获,剩下的才被可变参数捕获。

1
2
3
4
5
6
>>> def func(x,y=-1,*args):
print(x,y)
print(args)
>>> func(1,3,5,7)
1 3
(5,7)

第五,剩下的实参关键字参数被形参关键字参数捕获

如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义的函数如下:

1
2
def person(name, age, *, city, job):
print(name, age, city, job)

机理是,所有位置参数都已经被星号及其之前的形参接受,所以剩下的必须用关键字参数接收。

形参定义时,因该遵从的顺序

参数定义的顺序必须是:必选参数–>默认参数–>可变参数–>命名关键字参数–>关键字参数
关键字参数一定是放在最后的。

reference

Python中位置参数、默认参数、可变参数、命名关键字参数、关键字参数的区别

5. generator

  • 最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
  • generator is an iterator!
    official doc:

    generator: A function which returns a generator iterator.

Python之生成器详解:

我们常说的生成器,就是带有yield的函数,而generator iterator则是generator function的返回值,即一个generator对象,而形如mygenerator=(elem for elem in [1, 2, 3])括号中的表达式,称为generator expression,实际使用与generator无异。
iterator是消耗型的,用一次少一次

调用send(value)时要注意,要确保,generator是在yield处被暂停了,如此才能向yield表达式传值,否则将会报错(如上所示),可通过next()方法或send(None)使generator执行到yield.

send(none)起的作用相当于next()
generator可以用于for循环,例如:for i in mygenerator: # mygenerator的定义见上文

6. zip() & Unpacking Argument Lists——"*" & “**”

The implementation of zip is very beautiful:
来自python doc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def zip(*iterables):
# zip('ABCD', 'xy') --> Ax By
sentinel = object()
# ct: iterators is a "Iterator" point to the "Iterator" of different parameters(形参)
# e.g., at first, iterators is the "Iterator" of 'ABCM';
# iterators.next() is the "Iterator" of 'xy';
iterators = [iter(it) for it in iterables]
while iterators:
result = []
for it in iterators:
elem = next(it, sentinel)
if elem is sentinel:
return
result.append(elem)
yield tuple(result)
1
2
3
4
5
6
7
8
9
#zip() in conjunction with the * operator can be used to unzip a list:
>>> x = [1, 2, 3]
>>> y = [4, 5, 6]
>>> zipped = zip(x, y)
>>> list(zipped)
[(1, 4), (2, 5), (3, 6)]
>>> x2, y2 = zip(*zip(x, y))
>>> x == list(x2) and y == list(y2)
True
  • Unpacking Argument Lists
    1
    2
    3
    4
    5
    >>> list(range(3, 6)) # normal call with separate arguments
    [3, 4, 5]
    >>> args = [3, 6]
    >>> list(range(*args)) # call with arguments unpacked from a list
    [3, 4, 5]

7. Iterable和Iterator

  • Iterable
    凡是可作用于for循环的对象都是Iterable类型,它有__getitem__()方法;
    集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
  • Iterator
    Iterator is Iterable!
    凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列,它有__next__()和__iter__()方法
    理解:想一想C++中的Iterator,它就是个指针。
    Iterator是消耗型的,即每一个值被使用过后,就消失了,但是最终它并不等于None
  • while my_iterator(参见zip的实现)
    iterator最终会返回StopIteration对象,而while可以对其进行判断。

8. for

Python的for循环本质上就是通过不断调用next()函数实现的,例如:
for x in [1, 2, 3, 4, 5]:
pass
实际上完全等价于:
首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
然后在循环:

1
2
3
4
5
6
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环break

NOTE: The implement of for directly influence the corresponding results, e.g.:

  • example of for
    1
    2
    3
    4
    some_string = "wtf"
    some_dict = {}
    for i, some_dict[i] in enumerate(some_string):
    pass

Output of some_dict:
{0: 'w', 1: 't', 2: 'f'}
NOTE: This is because during for statement, dummy variables(e.g., i and some_dict[i] in present case) will be assigned in each loop.

8. 内存

8.1. 释放内存

先del再gc.collect()

9. Python类

9.1. 和静态语言不同,Python允许对实例变量绑定任何数据

也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同:

1
2
3
4
5
6
7
8
9
>>> bart = Student('Bart Simpson', 59)
>>> lisa = Student('Lisa Simpson', 87)
>>> bart.age = 8
>>> bart.age
8
>>> lisa.age
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'age'

9.2. Python的伪私有属性

Note that user defined attributes shall not end with __

9.3. 私有变量的访问方法

不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量。
注意下面的这种错误写法:

1
2
3
4
5
6
>>> bart = Student('Bart Simpson', 98)
>>> bart.get_name()
'Bart Simpson'
>>> bart.__name = 'New Name' # 设置__name变量!
>>> bart.__name
'New Name'

表面上看,外部代码“成功”地设置了__name变量,但实际上这个__name变量和class内部的__name变量不是一个变量!内部的__name变量已经被Python解释器自动改成了_Student__name,而外部代码给bart新增了一个__name变量。

9.4. 特殊函数__call__模糊了对象与函数的区别

9.5. 类类型的检查–不检查

静态语言 vs 动态语言
对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。

对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了:

1
2
class Timer(object):def run(self):
print('Start...')

这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

Python的“file-like object“就是一种鸭子类型。对真正的文件对象,它有一个read()方法,返回其内容。但是,许多对象,只要有read()方法,都被视为“file-like object“。许多函数接收的参数就是“file-like object“,你不一定要传入真正的文件对象,完全可以传入任何实现了read()方法的对象。

18. namespace and scope

What is the relationship between scope and namespaces in Python?
Difference between Scopes and Namespaces in python?

  • namespace

    A namespace is a dictionary, mapping names (as strings) to values. When you do an assignment, like a = 1, you’re mutating a namespace. When you make a reference, like print(a), Python looks through a list of namespaces to try and find one with the name as a key.

namespaces is a dict in deepdown.

We can picture a namespace as a Python dictionary structure, where the dictionary keys represent the names and the dictionary values the object itself (and this is also how namespaces are currently implemented in Python), e.g.,
a_namespace = {'name_a':object_1, 'name_b':object_2, ...}

A bad explanation:

You can think of “scope” as being the set of names that you have access to from a particular point in the code.

  • LEGB rule
    Local -> Enclosed -> Global -> Built-in
  • example (I’m not sure it’s right)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    x = 1
    y = 2
    def foo():
    z = 3 + y
    # P1 point !
    '''
    Here, I have access to `x, y, foo` -- They are in the current scope.
    `z` is not in the current scope. It is in the scope of `foo`.
    '''
    a = x + y # P2 point !

At ‘P1’ point:
scope:
scope includes x, y, foo and z, but not a.
namespace:
At foo namespace, foo includes z

At ‘P2’ point:
scope:
scope includes x, y, foo and a, but not z.
namespace:
namespace of this module/file includes x, y, foo and a, but not z.
NOTE: In ‘P2’ point, scope and namespace has the same included object.

16. 闭包

谈谈自己的理解:python中闭包,闭包的实质
example 1:

1
2
3
4
5
6
7
8
def outer( a ):
b = 10
# inner是内函数
def inner():
#在内函数中 用到了外函数的临时变量
print(a+b)
# 外函数的返回值是内函数的引用
return inner

内函数外函数在英文中也会写作enclosing function和enclosed function.

闭包中内函数修改外函数局部变量

example 2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def outer( a ):
# outer是外部函数 a和b都是外函数的临时变量
b = 10 # a和b都是闭包变量
c = [a] #这里对应修改闭包变量的方法2
def inner():
#内函数中想修改闭包变量
# 方法1 nonlocal关键字声明
nonlocal b
b+=1
# 方法二,把闭包变量修改成可变数据类型 比如列表
c[0] += 1
print(c[0])
print(b)
# 外函数的返回值是内函数的引用
return inner

my example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def record_order_to_csv(logger, order_in_pair_utctime, filename):
def decorate(func):
@wraps(func)
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
# in here, an error will be raised. To avoid that, aclaim nonlocal b, or just change the name : `fn = get_csv_name(filename)`
filename = get_csv_name(filename)
with open(filename, 'a+') as f:
f.write(', '.join(["some ", "thing"]) + "\n")
return res
return wrapper
return decorate

变量绑定给内函数

  1. 每次调用外函数,它都创建一个内函数,虽然代码一样,但是却创建了不同的对象
  2. 临时变量会绑定给内函数,再把内函数引用返回!

以example 1的代码为例,调用两次outer,两次返回inner都绑定了同一个变量b,而不是两个b的值!
这是一个经典的大坑!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
funcs = []
results = []
for x in range(7):
def some_func():
return x
funcs.append(some_func)
results.append(some_func())
funcs_results = [func() for func in funcs]
#Output:
>>> results
[0, 1, 2, 3, 4, 5, 6]
>>> funcs_results
[6, 6, 6, 6, 6, 6, 6]

这就是因为所有的some_func都绑定到了变量x上,而x的值是会变的。

解决方案:

1
2
def some_func(x=x):
return x

这样x就会变成some_func的内部变量,值就会传进来(我们知道,python中函数的默认值只是在定义时传入一次)。

有什么用?

  1. 做装饰器
    对已有的函数添加功能(以及变量)
  2. 面向对象
    将外函数的临时变量和内函数绑定,和面向对象中将变量和方法绑定,是如出一辙

10. 为什么 Python 的类不构成作用域(scope)? 待深入

10.1 scope search order:

顺序可以简记为legb(local, enclosing, global, built-ins)

  • Search the local scope
  • Search the scope of any enclosing functions
  • Search the global scope
  • Search the built-ins

    Only classes, functions, and modules provide scope in Python, so anything declared in an if block has the same scope as anything decleared outside the block.

10.2 the scope of a variable initialized in an if statement

In python the scope of a variable is veeery different with other languages.

Python variables are scoped to the innermost function, class, or module in which they’re assigned. Control blocks like if and while blocks don’t count, so a variable assigned inside an if is still scoped to a function, class, or module.
(Implicit functions defined by a generator expression or list/set/dict comprehension do count, as do lambda expressions. You can’t stuff an assignment statement into any of those, but lambda parameters and for clause targets are implicit assignment.)

e.g.:

1
2
3
4
for i in range(10):
x = 5
print(x)
# x is equal to 5!

10.3 exec与eval的奇特之处

1
2
3
4
5
6
7
def f(): #运行抛异常
a = 123
def g():
¦ exec("print(a)")
g()
f()

然而:

1
2
3
4
5
6
7
8
def f(): #运行正常
a = 123
def g():
¦ exec("print(a)")
¦ print(a)
g()
f()

10.4 注意区分list comprehension与generator!

  • list comprehension是当时计算的
  • list(a+i for i in range(10))中,里面属于generator,是一个匿名函数。

10.5 类的方法与普通函数的区别

运行到class这句的时候,会先将class下面的代码块“假装”当成一个函数来执行,但语法上和正常函数有一些区别:

  1. 无输入参数
  2. 不能显式return
  3. 类代码块中定义的函数,不视为类代码块的闭包,具体例子如下:
    1
    2
    3
    4
    5
    class A:
    a = 123
    def g():
    print a
    g()

会报错,函数g找不到a

正常的闭包:

1
2
3
4
5
def func():
a = 123
def g():
print a
g()

结果就不会报错

11. super and MRO

MRO: Method Resolution Order. For more details see this great introdcution: Python: 你不知道的 super

super 的一个最常见用法可以说是在子类中调用父类的初始化方法了,比如:

1
2
3
4
class A(Base):
def __init__(self, a, b, c):
super(A, self).__init__(a, b) # Python3 可使用 super().__init__(a, b)
self.c = c

实际上,super(cls, inst) 获得的是 cls 在 inst 的 MRO 列表中的下一个类。

12. Python Interpreter

  • CPython
    IPython based on CPython, and is enhanced in interactive.
  • PyPy
    Use Jit, and can be different with CPython.
  • Jython
    Compile python to Java bytecode.
  • IronPython
    Compile python to .net bytecode

13. Python3 String

13.1 unicode

  • All the python3 string is unicode string.
    e.g.:
    print('\u5730\u5740\u9519\u8bef')会显示地址错误
    NOTE: Unicode is different from utf-8 code! Unicode编码及其实现:UTF-16、UTF-8
  • Real Unicode
    Many introduction about unicode is wrong, even for the Baidu Encyclopedia.
    Japanese: unicode 3040-31FF
    Unicode(UTF-8, UTF-16)令人混淆的概念 must be read very carefully.

    Unicode涉及到两个步骤,首先是定义一个规范,第二步才是怎么把字符对应的数字保存在计算机中。
    UTF-16就是任何字符对应的数字都用两个字节来保存
    UTF-8,这里的8非常容易误导人,用UTF-8时表示一个字符是可变的,有可能是用一个字节表示一个字符,也可能是两个,三个.当然最多不能超过3个字节了。
    UTF-32就是把所有的字符都用32bit.
    UCS就是前面说的ISO制定的标准,和Unicode是完全一样的,只不过名字不一样.ucs-2对应utf-16,ucs-4对应UTF-32.UTF-8是没有对应的UCS.

BeautifulSoup4 will automatically find the encoding of HTML page (based on the info in the HTML head), and use this encoding to decode the r.text.
However, if encoding is not found, encoding will be considered as “ISO-8859-1”, whether it’s because of failing parse or non-existence,

1
2
3
4
print(r.encoding)
text = r.text
text.encode("ISO-8859-1","ignore") == r.content
# result is "ISO-8859-1" and "True"
  • two examples:
    "严"字
    "き"字
    • For “き”: It’s python3 source code is u"\u304D"
    • utf-8 is 0xE3 0x81 0x8D (e3818d);
    • utf-16 is 0x304D (304d);
    • utf-32 is 0x0000304D (304d)
    • HTML Entity (hex) &#x304d;
  • code
    1
    2
    3
    one_char = 'き'
    print(one_char, one_char.encode("utf-8"),one_char.encode("utf-16"), one_char.encode("utf-32"))
    print(one_char.encode('unicode_escape'), one_char.encode('unicode_escape').decode('unicode_escape'))

结果如下:

1
2
b'\xe3\x81\x8d' b'\xff\xfeM0' b'\xff\xfe\x00\x00M0\x00\x00'
b'\\u304d'

13.2 hex

  • decode a hex string
    十六进制可以看成一种编码方式。实际使用中,我们一般用到的是hex string,比如ethereum RPC的返回值:
    res = '0x000000a2'
    res为例,所谓hex string,就是说res在python中的类型还是string,但显然它是一个16进制编码过的string。
    那么我们要得到真实信息,就需要解码;要解码,就需要知道res原本是什么类型。(比如0x41,解码成整数,那就是整数65;但按照解码ascii,第65个ascii字符,就是小写字母a)

  • HexBytes type to hex string
    HexBytes is a built-in type, e.g.:

    1
    2
    3
    from web3 import Web3
    # web3.__version__ == '4.4.1'
    res = Web3.sha3(text="numberOfProposals()")

The type of res is HexBytes. To get a hex string, just use res.hex(), then a string will be returned.

14. python path

There’re two kind of path: executing path (EP) and python file path (PFP). e.g.:

1
2
$ ls test/
test_all.py

Then EP is ., PFP is ./test/!

  • The sys.path contains PFP, not EP.
  • In from * import *, PEP is used, i.e., it’s ‘from’ ./test/ dir.

一个module应该只有一种方式import

有一种说法是:python中的包只会被import一次
但是,from logger import loggerfrom log.logger import logger明明是一个module,python却会导入两次!
ref: python中的module不一定是单例,有可能会被多次import成多个module

15. __slots__

First of all, python allow run-time adding attributes and methods to a defined class or a instance of defined class. e.g.:

1
2
3
4
5
6
7
8
# Student is a defined class
def set_score(self, score):
self.score = score
Student.set_score = set_score
s = Student()
s.set_score = set_score

Then to restrict the attributes, __slots__ is defined so that only the attributes in __slots__ can be defined in class.
e.g.:
__slots__ = ('name', 'age') # name and age can be defined in present class
__slots__ = () # none attribute can be defined.

使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__

17. myth of code object

探索 Python 代码对象
三个层次:function object -> code object -> stack-based bytecode(字节码)

CPython 实现了一个执行基于栈的字节码 (stack-based bytecode) 的虚拟机。
在运行时,任何可执行的东西 (函数、方法、模块、类主体 (class body)、Lambda 式、语句、表达式等等) 都以字节码的形式由 Python 虚拟机执行。

1
2
def f(a = b):
print a
  • f的代码块在编译时被编译为一个code object放在字节码中,然后执行到这里的时候,根据这个code object构建一个function object,然后,将这个function object赋值给f这个变量。
  • function object还含有一些动态的信息,比如函数参数默认值(上面的b),或是否闭包的信息(若f是一个闭包函数)等

19. decorator

Decorator is just a syntactic sugar:

1
2
@my_decorator
def func:

Above code is just the same as: func = my_decorator(func)

basic version

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import time
from functools import wraps
def timethis(func):
'''
Decorator that reports the execution time.
'''
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(func.__name__, end-start)
return result
return wrapper
  • 机理的理解:

    1. decorator实际相当于:new_func = timethis(func)
    2. timethis返回了wrapper函数,所以new_func实际上就是timethis中的wrapper函数
    3. 调用new_func时的参数都传给了wrapper
  • 调用未包装的原始函数:
    使用了@wraps或者直接设置了__wrapped__属性,那么new_func.__wrapped__就是原函数

  • 如果有多个包装器,那么访问__wrapped__属性的行为是不可预知的,应该避免这样做。
    在Python3.4中,它会如我们预期的一样。
    在Python3.3中,它会略过所有的包装层。

Style guide

argument

逻辑判断

布尔测试应该很简单,如果它们很复杂的话,你需要将它们的运算事先放到一个变量里,并
且为变量取一个好名字 – [learn python the hard way]

add static variable to function

There are many different ways to add a static variable in a function. Below one happens to be a good way:

1
2
3
def func():
if not hasattr(func, 'x'):
func.x = "lala"

TIPS

  • you need to flush stdout periodically (e.g. sys.stdout.flush()). Python doesn't automatically do this even with print until the program exits.

  • id(WTF()) == id(WTF()) result is True [1]
    id(WTF()) will (1) instantiate a WTC object and (2) get the id of this object and (3) destroy this object.
    Hence this two objects has same id.

  • Evaluation time discrepancy 非常重要!

    1
    2
    3
    4
    5
    6
    array = [1, 8, 15]
    g = (x for x in array if array.count(x) > 0)
    array = [2, 8, 22]
    #Output:
    >>> print(list(g))
    [8]
  • A tic-tac-toe where X wins in the first attempt!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    '''
    我们先初始化一个变量row
    '''
    row = [""]*3 #row i['', '', '']
    board = [row]*3 # 并创建一个变量board
    #Output:
    >>> board
    [['', '', ''], ['', '', ''], ['', '', '']]
    >>> board[0][0]
    ''
    >>> board[0][0] = "X"
    >>> board
    [['X', '', ''], ['X', '', ''], ['X', '', '']]

Right way to do it is :
>>> board = [['']*3 for _ in range(3)]

  • ==的优先级高于not

  • unbounded method不太懂,不过在python3中已经完全抛弃了

  • check whether a variable is a function
    isinstance(func, (types.FunctionType, types.BuiltinFunctionType, functools.partial))

thinking

  • Abstact leak永远会存在。
    换言之,永远都需要向下一层,去理解当前用到的技术是如何实现的。
    以Evaluation time discrepancy为例:
    for statement has a simple implement, in statement has a simple implement too.
    然而,当几个简单的语句组合在一起,复杂性就产生了。除了语言的自洽性和正确性,又需要考虑到性能、异步等问题。

For more:

[我眼中一个好的Pythoneer应该具备的品质]*(https://zhuanlan.zhihu.com/p/33266239)
Python进阶 - gitbook


  1. What the f*ck Python!

谢谢~