Zabbix监控安装
Zabbix作为一款老牌监控,在服务器规模还不是很大时很有用。
安装Zabbix有很多种方式,这里使用rpm方式安装。因为服务器是CentOS 32位的,所以选择下面的rpm包,相应的包可以在阿里云上找到。rpm -ivh http://mirrors.aliyun.com/zabbix/zabbix/3.0/rhel/6/i386/zabbix-release-3.0-1.el6.noarch.rpm, 具体安装见参考资料。
参考资料
Zabbix作为一款老牌监控,在服务器规模还不是很大时很有用。
安装Zabbix有很多种方式,这里使用rpm方式安装。因为服务器是CentOS 32位的,所以选择下面的rpm包,相应的包可以在阿里云上找到。rpm -ivh http://mirrors.aliyun.com/zabbix/zabbix/3.0/rhel/6/i386/zabbix-release-3.0-1.el6.noarch.rpm, 具体安装见参考资料。
可迭代的和迭代器是两个经常容易混淆的概念,这里说说这两个概念
任何调用iter函数可以获得一个迭代器的都是可迭代的。实现__iter__方法,返回一个迭代器的对象是可迭代的;序列是可迭代的,因为它们实现了__getitem__方法。
iter函数执行时会做如下操作
__iter__, 从其中获得一个迭代器__iter__方法,查看是否实现__getitem__方法,从中创建一个迭代器,从0开始获取每一个元素。正是因为这一点,序列是可迭代的当需要自己实现的迭代对象时,需要实现__iter__方法,因为从__getitem__中创建迭代器是为了向后兼容,未来也许会被废弃。
任何实现无参next方法,每次返回一个元素,当没有更多元素时,返回StopIteration异常的对象,都是迭代器。迭代器也实现了iter方法,所以也是可迭代的。也就是迭代器提供了访问可迭代对象的接口,用户不需要知道其内部实现。
迭代器模式的用途主要是
为了支持多次遍历,调用iter函数时,需要返回一个全新的迭代器。如果可迭代器对象变成一个迭代器,那么它将返回原先的迭代器,它将只支持一次遍历。
在《Fluent Python》里看到Why len is not method,有些困惑,于是开始找答案。
在Python词汇表里,对函数和方法有如下说明
function: A series of statements which returns some value to a caller. It can also be passed zero or more arguments which may be used in the execution of the body.
method: A function which is defined inside a class body. If called as an attribute of an instance of that class, the method will get the instance object as its first argument (which is usually called self).
简单来说,就是函数在类里定义就叫方法。所以这里len是一个函数,而不是方法。那么问题是,为什么不把len做成方法,像list.len() str.len()这样呢?
在What makes Python so AWESOME里《Fluent Python》的作者Luciano Ramalho向Raymond Hettinger提了这个问题,Raymond Hettinger说是因为”practicality beats purity”, 因为把len做成一个函数更加直观和符合直觉。
实际实现时,len函数对于内置类型,会去C底层实现的结构体里取一个字段值,这个字段值记录了长度。而对于用户自定义类型,用户实现len方法的话,len函数会去调用len方法。
因为新公司要使用Flask开发,于是需要熟悉它。找到《Flask Web Developmen》这本书,于是开始学习使用Flask编写Web站点。
想到把之前用Django开发的站点,爱与生的苦恼还不够完善,于是决定用Flask重写。之所以叫爱与生的苦恼,是因为之前看过叔本华写的《爱与生的苦恼》。想想每个人很多时候都是在爱与恨中度过,就像我喜欢跑步,囚徒健身,现代牧业,旭辉控股,莱尔斯丹等等,讨厌工商银行,赌博,垄断,Windows命令行等等。于是我想把它记下来,也可以看看自己喜欢与讨厌的变化。或许随着时间的推移,自己会变得更加宽容,更加开放。也许那个时候,不在讨厌工商银行,Windows命令行也是有可能的。记微信朋友圈显得有些杂乱,于是干脆记在自己写的网站里。
《Flask Web Development》的作者Miguel Grinberg显然具有多年开发经验,将Web开发需要注意的地方,通过一个完整的博客应用娓娓道来,令人倾佩。许多代码稍作更改,就可以用在自己的站点中,大呼过瘾。如果要学习Flask开发,极力推荐看看这本书。
相比于Django, Flask显得更加轻量。没有了Django的admin,但好在应用比较简单,写个简单的后台没有什么问题。基本上Django里有的功能,Flask都能找到合适替代品。整个开发过程还是很顺畅的,唯一不好的一点是Flask的migrate功能有问题,当使用SQLite做数据库时,自动生成的SQL语句会出问题,主要是SQLite不支持Alter字段。
刚开始时,访问速度很慢,因为显示的都是原始图片,动不动就是10M,用户体验不是很好。曾考虑过使用七牛图床,只是这样增加了开发成本,而且需要把照片寄存在第三方应用中,好处是图片清晰。考虑之后,还是决定使用Pillow模块进行图片裁剪,目前来看,效果还不错。
之后添加了粉丝,评论功能这些社交元素。除了界面显示效果略差,整体效果还算满意。
站点地址可移步爱与生的苦恼, 欢迎在此记录爱与生的苦恼。
这是一道关于元组+=赋值的谜题, 据说,能答对这道题的都是Python老司机。 请在不执行下面的代码的情况下,回答这个问题
1 | >>> t = (1,2,[30,40]) |
看到这题时,第一反应是b。之后使用dis模块查看执行语句, 得到
1 | >>> dis.dis("t[2] += [50, 60]") |
可以看到第17条的INPLACE_ADD操作,因为列表是可变对象,所以这条会执行成功。之后第19条进行保存,而元祖是不可变对象,所以第19条会报错。
所以答案是d, 即a和b都发生了。
在Python词汇表里对,可哈希的有这样一段说明:
An object is hashable if it has a hash value which never changes during its lifetime (it needs a hash() method), and can be compared to other objects (it needs an eq() method). Hashable objects which compare equal must have the same hash value.
也就是说,一个对象的哈希值(实现hash方法)在对象的生命周期里永远不变的,这个对象可以与其它对象进行比较(实现eq方法), 相等的对象哈希值一定相等,那么这个对象就是可哈希的。
也就是说,可哈希对象必须满足下面三个条件
这里不明白的是第三点,也就是为什么两个对象相等,哈希值一定要相等,不相等难道不行吗?
下面编写的一个测试类
1 | class UnHashable(object): |
如果我们执行
1 | d = dict() |
之后我们会希望d[b]返回4,但是并不会如此,因为hash[a]并不一定等于hash[b]. 这就是为什么要求可哈希对象一定要a == b时,hash(a) == hash(b)
在Python中,对于内置不可变对象,都是可哈希的,而字典和列表等可变对象不是可哈希的。
在《Fluent Python》里说, A decorator is a callable that takes another function as argument。也就是装饰器就是一个可执行对象,它的参数是一个函数。
1 | @decorate |
相当于
1 | def target(): |
另一个重要的问题是当模块导入时,装饰器就执行了。
编写registration.py
1 | registry = [] |
之后导入模块,可以看到registration.registry的值
1 | >>> import registration |
下面编写一个记录函数执行时间的装饰器,保存到clockdeco.py中
1 | import time |
之后使用这个装饰器
1 | import time |
可以看到结果
1 | **************************************** Calling snooze(123) [0.12405610s] snooze(.123) -> None **************************************** Calling factorial(6) [0.00000191s] factorial(1) -> 1 |
上面编写的clock装饰器存在一个问题是不支持关键字参数,可以使用标准库里的functools.wraps来解决这个问题。
1 | import time |
Python内置了三个常用的装饰器,property,classmethod和staticmethod,
标准库functools里还有两个有趣的装饰器lru_cache和singledispatch。
要编写带参数的装饰器,一个解决的办法是编写一个装饰器工厂,然后根据参数不同,返回不同的装饰器。
拿上面的clock装饰器来说,如果要允许传入时间格式来输出不同的日期格式,可以如下编写装饰器工厂, 保存到clockdeco_param.py中
1 | import time |
编写如下测试代码,可以看到不同的时间格式。
1 | import time |
输出结果如下
1 | [0.12581110s] snooze(0.123) -> None |
《Fluent Python》里说,简单来说,闭包就是一个包含非全局变量且该变量不是在它自己函数体里声明的函数。它不关心函数是否匿名,它关心的是可以访问不在自己函数体里定义的非全局变量。
考虑一个avg平均值函数,如下
1 | >>> avg(10) |
一个使用类的方法如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class Averager():
"""
>>> avg = Averager()
>>> avg(10)
10.0
>>> avg(11)
10.5
>>> avg(12)
11.0
"""
def __init__(self):
self.series = []
def __call__(self, new_value):
self.series.append(new_value)
total = sum(self.series)
return total/len(self.series)
一个函数的实现方法如下
1 | def make_averager(): |
这里需要注意的是series相对于averager是一个自由变量,也就是这个变量不属于局部变量。
1 | >>> avg.__code__.co_varnames ('new_value', 'total') |
而每一个avg.__code__.co_freevars里的变量的值都保存在avg.__closure__中,其值在avg.__closure__[0].cell_contents
1 | >>> avg.__code__.co_freevars |
所以,闭包就是一个可以从自由变量里获取变量的函数。
下面看看为什么会引入nonlocal这个关键字,以及它的作用。
如果我们想办法消除series变量,可以编写如下函数。
1 | def make_averager(): |
但函数执行时会报错
1 | >>> avg = make_averager() |
这是因为变量作用域的原因。count += 1相当于count = count + 1, 此时count会被当做局部变量,而不是引用外面一层的count, 而averager函数里并没有声明count变量,于是报错。可以参看Python之dis模块
在Python3里添加了nonlocal来解决这个问题。
1 | def make_averager(): |
dis, 即Disassembler for Python bytecode, 用于Python字节码的反汇编。可以用于查看函数的具体执行步骤,例如下面的局部变量的例子
1 | def f1(a): |
得到
1 | 3 0 LOAD_GLOBAL 0 (print) |
执行
1 | a = 3 |
不会报错,这里第13条是LOAD_GLOBAL,此时有全局变量b.
再看这个例子
1 | def f2(a): |
得到的是
1 | 4 0 LOAD_GLOBAL 0 (print) |
执行
1 | a = 3 |
会报找不到局部变量b, 这是因为第13条LOAD_FAST是加载局部变量,此时还没有局部变量b.
《Fluent Python》中写到,在使用Python字典时,有两种方式,d[key]与d.get(key),它们有一些细微的区别
d[key]底层实现是调用dict.__getitem__, 而d.get(key)就是一个函数调用。
当dict.__getitem__ 没有找到key时,会调用dict.__missing__
执行如下代码1
2
3
4from dis import dis
d = dict()
print(dis('d["a"]'))
print(dis('d.get("a")'))
输出如下结果
1 | 1 0 LOAD_NAME 0 (d) |
可以看到get是函数调用,而d[]不是。
编写测试代码如下
1 | class Dict(dict): |
输出结果如下
1 | test 2 None 2 |