用classmethod装饰器修饰的函数,第一个参数是类,classmethod的最常见用途是类构造方法

staticmethod实际上并没什么用,使用函数就可以完成相应的功能。

编写如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
class Demo(object):
@classmethod
def klassmeth(*args):
return args # ... @staticmethod

def statmeth(*args):
return args #

if __name__ == "__main__":
print(Demo.klassmeth())
print(Demo.klassmeth('spam'))
print(Demo.statmeth())
print(Demo.statmeth('spam'))

输出结果如下

1
2
3
4
(<class '__main__.Demo'>,)
(<class '__main__.Demo'>, 'spam')
()
('spam',)

在使用Python的thrfit库时,提示No handlers could be found for logger “thrift.server.TServer”,莫名奇妙,加了logging配置后,logging.basicConfig(level=logging.INFO), 问题就解决了。

在项目中,有一个需求是分配IP,在分配之前,需要判断IP是否已经被使用,也就是判断是否能PING通。

因为使用的开发语音是Python,于是想到使用Python的ping库,但发现需要root权限。之后又想到通过subprocess调用ping命令,能满足需求,但是因为一次需要分配多个IP,于是需要并发执行,这样就需要使用多进程或者多线程,于是暂时先放着。

后来同事说可以使用fping, 因为它支持多个IP地址同时ping, 然后返回可以ping通的IP地址,正好满足这个需求。

查了fping的帮助, 加上-a和-A参数,一个简单的例子如
fping -aA 115.239.210.27 115.236.139.174

现在想来,很多时候工程实践能减少代码的编写就减少,这个场景使用fping这种工具,就是将需要编写的并发代码交给了工具完成。

看blinker源码时,看到weakref,也就是弱引用模块,以前看《Fluent Python》时也有看到过,只是没有再实际场景中用过。看介绍,主要是用在缓存时对象释放,以及循环引用等场景。PEP-205有结束这个模块的由来。

下面是一个缓存使用中的例子,先声明一个Cheese类

1
2
3
4
5
6
7
class Cheese(object):

def __init__(self, kind):
self.kind = kind

def __repr__(self):
return 'Cheese(%r)' % self.kind

之后在交互环境中执行如下操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> import weakref
>>> stock = weakref.WeakValueDictionary()
>>> catalog = [Cheese('Red Leicester'), Cheese('Tilsit'), Cheese('Brie'), Cheese('Parmesan')]
>>> for cheese in catalog:
... stock[cheese.kind] = cheese
...
>>> sorted(stock.keys())
['Brie', 'Parmesan', 'Red Leicester', 'Tilsit']
>>> del catalog
>>> sorted(stock.keys())
['Parmesan']
>>> del cheese
>>> sorted(stock.keys())
[]

可以看到当删除catelog对象时,在stock中的对象会自动释放。

对于循环引用的例子,可以参考Python 弱引用的使用

在Blinker的源码里看到一个defaultdict实现,记录下来

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
class defaultdict(dict):

def __init__(self, default_factory=None, *a, **kw):
if (default_factory is not None and
not hasattr(default_factory, '__call__')):
raise TypeError('first argument must be callable')
dict.__init__(self, *a, **kw)
self.default_factory = default_factory

def __getitem__(self, key):
try:
return dict.__getitem__(self, key)
except KeyError:
return self.__missing__(key)

def __missing__(self, key):
if self.default_factory is None:
raise KeyError(key)
self[key] = value = self.default_factory()
return value

def __reduce__(self):
if self.default_factory is None:
args = tuple()
else:
args = self.default_factory,
return type(self), args, None, None, self.items()

def copy(self):
return self.__copy__()

def __copy__(self):
return type(self)(self.default_factory, self)

def __deepcopy__(self, memo):
import copy
return type(self)(self.default_factory,
copy.deepcopy(self.items()))

def __repr__(self):
return 'defaultdict(%s, %s)' % (self.default_factory,
dict.__repr__(self))

Flask使用Blinker来处理信号订阅和发送, 查看文档Blinker看看简单的使用方法。

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
from blinker import signal

def subscriber(sender):
print("Got a signal sent by %r" % sender)


class Processor:
def __init__(self, name):
self.name = name

def go(self):
ready = signal('ready')
temp = ready.send(self)
print(temp)
print("Processing.")
complete = signal('complete')
complete.send(self)

def __repr__(self):
return '<Processor %s>' % self.name

ready = signal('ready')
ready.connect(subscriber)
temp = ready.send('sdfsdf')
print(temp)
processor_a = Processor('a')
processor_a.go()

使用signal创建一个信号, connect添加一个订阅,send发送信号,send的返回值是一个数组,数组元素是元祖,包含信号接收者和信号接收后的返回结果。

JQuery的事件代理主要用来解决通过js添加的HTML元素无法监听事件的问题。

JQuery event delegation中给了如下一个简单例子,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<html>
<head>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
</head>
<body>
<div id="container">
<ul id="list">
<li><a href="http://domain1.com">Item #1</a></li>
<li><a href="/local/path/1">Item #2</a></li>
<li><a href="/local/path/2">Item #3</a></li>
<li><a href="http://domain4.com">Item #4</a></li>
</ul>
</div>
<script>
$( "#list a" ).on( "click", function( event ) {
event.preventDefault();
console.log( $( this ).text() );
});
$( "#list" ).append( "<li><a href='http://newdomain.com'>Item #5</a></li>" );
</script>

</body>
</html>

点击item1到item4,对于click事件都会有响应,在console中可以看到输出,而点击item5时click事件不会响应,这是因为将click事件绑定时,item5还没有生成。

解决这个问题的方法是使用事件代理,原理是利用了事件传播机制。也就是在某一个元素中的事件,如果没有得到处理,一直会向父节点触发,直到根节点。如上面的a元素的click事件,如果没有被捕获,一直会向上触发,路径如下

  • <a>
  • <li>
  • <ul #list>
  • <div #container>
  • <body>
  • <html>
  • document root

<ul #list>元素上添加click事件,就可以看到效果。

1
2
3
4
$( "#list" ).on( "click", function( event ) {
event.preventDefault();
console.log( $( this ).text() );
});

最后,如果使用事件代理,我们可以把它绑定到<ul #list>元素,然后通过’a’去找到<a>元素,这样就可以监听通过js添加的HTML元素。

1
2
3
4
$( "#list" ).on( "click",  'a', function( event ) {
event.preventDefault();
console.log( $( this ).text() );
});

时间日期选择器可以使用bootstrap-datetimepicker, 中文文档见Bootstrap日期和时间表单组件

使用时有一些注意点,其中一点是不要使用datetimepicker class, 会出bug,主要现象是两个时间框叠在一起。

对于不同的时间输入框,要独立初始化,如下面两个输入框

1
2
3
4
5
6
7
8
9
10
11
12
<div class="row">
<label for="begin-time" class="col-sm-2 control-label">开始时间</label>
<div class="col-sm-4">
<input id="begin-time" readonly type="text" class="form-control" data-date-format="yyyy-mm-dd hh:ii:ss">
</div>
</div>
<div class="row">
<label for="end-time" class="col-sm-2 control-label">结束时间</label>
<div class="col-sm-4">
<input id="end-time" readonly type="text" class="form-control" data-date-format="yyyy-mm-dd hh:ii:ss">
</div>
</div>

要像如下独立初始化

1
2
3
4
5
6
$('#begin-time').datetimepicker({
language: 'zh-CN'
});
$('#end-time').datetimepicker({
language: 'zh-CN'
});

对于中文,还需要加载ootstrap-datetimepicker.zh-CN.js

需要给每个user添加一个leader, 于是在user表里添加leader, 而leader也是一个user, 于是构成了自引用。

1
2
3
4
5
6
7
8
9
10
11
class User(db.Model):
__tablename__ = 'users'
id = db.Column(Integer, primary_key=True)
name = db.Column(String(10))
addresses = relationship("Address", back_populates="user")
leader_id = db.Column(Integer, ForeignKey('users.id'))
users = relationship("User", back_populates="leader")
leader = relationship("User", back_populates="users", remote_side=[id])

def __repr__(self):
return u'<user id={0}, name={1}>'.format(self.id, self.name).encode('utf-8')

提示如下错误,

ArgumentError: User.users and back-reference User.leader are both of the same direction symbol(‘MANYTOONE’). Did you mean to set remote_side on the many-to-one side ?

最后参考StackOverFlow上One to one self relationship in SQLAlchemySQLAlchemy One-to-Many relationship on single table inheritance - declarative
添加remote_side解决问题.

backref和back_populates在表示两个表之间的关系时,很有用。

查看backref的文档,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class User(db.Model):
__tablename__ = 'users'
id = db.Column(Integer, primary_key=True)
name = db.Column(String(10))
addresses = relationship("Address", backref="user")

def __repr__(self):
return u'<user id={0}, name={1}>'.format(self.id, self.name).encode('utf-8')


class Address(db.Model):
__tablename__ = 'addresses'
id = db.Column(Integer, primary_key=True)
email = db.Column(String(64))
user_id = db.Column(Integer, ForeignKey('users.id'))

def __repr__(self):
return '<address id={0}, email={1} user_id={2}>'.format(self.id, self.email, self.user_id)

之后在命令行中,可以得到如下结果

1
2
3
4
5
6
7
8
9
10
11
12
>>> u = User(name='Bob')
>>> db.session.add(u)
>>> db.session.commit()
>>> u.addresses
[]
>>> a = Address(email='bob@163.com', user_id=u.id)
>>> db.session.add(a)
>>> db.session.commit()
>>> u.addresses
[<address id=2, email=bob@163.com user_id=2>]
>>> a.user
<user id=2, name=Bob>

即通过u.addresses可以访问到用户的addresses, 而a.user可以访问到用户。注意到u.addresses返回的是列表,而a.user返回的是单个元素,即User与Address是一对多的关系。

也可以使用back_populates实现相同的功能,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class User(db.Model):
__tablename__ = 'users'
id = db.Column(Integer, primary_key=True)
name = db.Column(String(10))
addresses = relationship("Address", back_populates = "user")

def __repr__(self):
return u'<user id={0}, name={1}>'.format(self.id, self.name).encode('utf-8')


class Address(db.Model):
__tablename__ = 'addresses'
id = db.Column(Integer, primary_key=True)
email = db.Column(String(64))
user_id = db.Column(Integer, ForeignKey('users.id'))
user = relationship("User", back_populates="addresses")

def __repr__(self):
return '<address id={0}, email={1} user_id={2}>'.format(self.id, self.email, self.user_id)

在交互环境中,得到如下结果

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> u = User(name='Clack')
>>> db.session.add(u)
>>> db.session.commit()
>>> u.addresses
[]
>>> a = Address(emial='Clack@163.com', user_id=u.id)
>>> a = Address(email='Clack@163.com', user_id=u.id)
>>> db.session.add(a)
>>> db.session.commit()
>>> u.addresses
[<address id=3, email=Clack@163.com user_id=3>]
>>> a.user
<user id=3, name=Clack>

从文档中得知,back_populates是用来取代backref的,虽然backref也是一直支持使用。倾向于使用back_populates, 因为它比backref更直接明了。