Fisher's Blog

Sein heißt Werden
Leben heißt Lernen

0%

Python Tricks

  • 拆箱
  • 使用 .(dot) 来访问字典成员
  • numpy 一维数组与二维数组相加
  • class 中的 self
  • global & nonlocal
  • 默认参数陷阱
  • 交换变量
  • 行内 if 语句
  • 带索引的列表迭代
  • … 持续更新中

拆箱

1
2
3
4
5
6
7
>>> a, *b, c = [1, 2, 3, 4, 5]
>>> a
1
>>> b
[2, 3, 4]
>>> c
5

使用 .(dot) 来访问字典成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Map(dict):
def __init__(self, *args, **kwargs):
super(Map, self).__init__(*args, **kwargs)
for arg in args:
if isinstance(arg, dict):
for k, v in arg.items():
self[k] = v

if kwargs:
for k, v in kwargs.items():
self[k] = v

def __getattr__(self, attr):
return self.get(attr)

def __setattr__(self, key, value):
self.__setitem__(key, value)

def __setitem__(self, key, value):
super(Map, self).__setitem__(key, value)
self.__dict__.update({key: value})

def __delattr__(self, item):
self.__delitem__(item)

def __delitem__(self, key):
super(Map, self).__delitem__(key)
del self.__dict__[key]


m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
# Add new key
m.new_key = 'Hello world!'
# Or
m['new_key'] = 'Hello world!'
print(m.new_key)
print(m['new_key'])
# Update values
m.new_key = 'Yay!'
# Or
m['new_key'] = 'Yay!'
# Delete key
del m.new_key
# Or
del m['new_key']

或者使用一个简化版的:

1
2
3
4
5
6
7
8
9
10
11
class dict2(dict):
def __init__(self, **kwargs):
dict.__init__(self, kwargs)
self.__dict__ = self

config = dict2(**{
"timesteps_per_batch": 1000,
"max_pathlength": 10000,
"max_kl": 0.01,
"cg_damping": 0.1,
"gamma": 0.95})

numpy 一维数组与二维数组相加

在写 tensorflow 时,dense 层的输出是一个二维数组,假设为 \(n \times 1\)Tensor ,而此时与另一个一维 Tensor 相加,则会出现意想不到的问题。用 numpy 的矩阵举个例子:

1
2
3
4
5
6
7
8
>>> import numpy as np
>>>
>>> a = np.array([1, 2, 3])
>>> b = np.array([[1], [2], [3]])
>>> print(a + b)
[[2 3 4]
[3 4 5]
[4 5 6]]

结果是个 \(3 \times 3\) 的矩阵,难怪程序不收敛。所以在实际写程序的过程中,尽量设置相同 Tensor 的维数。

class 中的 self

类中所有函数无论有没有参数,在定义成员函数时必须接受一个 self 参数:

1
2
3
4
5
6
class foo(object):
def bar(self):
pass

def lee(self, *args, **kwargs):
pass

global & nonlocal

global

当在局部作用域中要使用全局变量时,要使用 global 来修饰全局变量,如果不需要修改全局变量,也可以不使用 global 关键字。

1
2
3
4
5
6
7
8
9
10
11
g = 0


def foo():
g = 10
print('local', g)


print('global', g)
foo()
print('global', g)

输出:

1
2
3
global 0
local 10
global 0

这时第5行的 g 为局部变量,尽管与全局变量 g 的名字相同,但是两个不同的变量。如果要修改全局变量,则要加上 global

1
2
3
4
def foo():
global g
g = 10
print('local', g)

输出:

1
2
3
global 0
local 10
global 10

nonlocal

nonlocalglobal 类似,nonlocal 用来修饰外层(非全局)变量。

1
2
3
4
5
6
7
8
9
10
11
12
def foo():
g = 0

def bar():
g = 10
print('bar', g)

bar()
print('foo', g)


foo()

输出:

1
2
bar 10
foo 0

如果要修改外部变量 g 则要加上 nonlocal 关键字:

1
2
3
4
def bar():
nonlocal g
g = 10
print('bar', g)

输出:

1
2
bar 10
foo 10

默认参数陷阱

1
2
3
4
5
6
7
def foo(arr=[], el=None):
arr.append(el)
print(arr)


foo(el=1)
foo(el=2)

对于这样一个函数,其中有默认参数 arr=[] ,如果是 javascript 或其他编程语言的话,每次调用函数都是默认把一个空 list 赋值给 arr ,但 python 不是这么做的,它的输出为:

1
2
[1]
[1, 2]

因为 python 函数的参数默认值,是在编译阶段就确定的,之后所有的函数调用,如果参数不显示的赋值,默认参数都是指向在编译时就确定的对象指针。

对于普通的不可变变量 int , string , float , tuple,在函数体内如果修改了该参数,那么参数就会重新指向另一个新的不可变值。

但对于可变对象 list , dict ,所有对默认参数的修改实际上都是对编译时已经确定的那个对象的修改。

交换变量

1
2
3
4
5
6
>>> x = 1
>>> y = 2
>>>
>>> x, y = y, x
>>> print(x, y)
2 1

行内 if 语句

1
2
3
4
>>> print("Hello" if True else "World")
Hello
>>> print("Hello") if False else print("World")
World

带索引的列表迭代

1
2
3
4
5
6
7
>>> arr = ['foo', 'bar', 'lee']
>>> for index, el in enumerate(arr):
... print(index, el)
...
0 foo
1 bar
2 lee