解决centos7中python-pip模块不存在的问题

entos 7中python-pip模块不存在,是因为像centos这类衍生的发行版,源跟新滞后,或者不存在。即使使用yum去search python-pip也找不到软件包。

为了使用安装滞后或源中不存在的安装包,需要安装扩展源EPEL。扩展源EPEL(http://fedoraproject.org/wiki/EPEL) 是由 Fedora 社区打造,为 RHEL 及衍生发行版如 CentOS、Scientific Linux 等提供高质量软件包的项目。

安装扩展源:sudo yum -y install epel-release

安装python-pip模块:sudo yum install python-pip

 

Angularjs 学习笔记

ng-init
  1. <div ng-app="" ng-init="firstName='John'">
  2. <p>姓名为 <span ng-bind="firstName"></span></p>
  3. </div>
带有有效的 HTML5:data-ng-bind
  1. <div data-ng-app="" data-ng-init="firstName='John'">
  2. <p>姓名为 <span data-ng-bind="firstName"></span></p>
  3. </div>
AngularJS 表达式写在双大括号内:{{ expression }}

AngularJS 表达式 很像 JavaScript 表达式:它们可以包含文字、运算符和变量。

实例 {{ 5 + 5 }} 或 {{ firstName + ” ” + lastName }}

  1. <!DOCTYPE html>
  2. <html>
  3. <body>
  4. <div ng-app="">
  5. <p>我的第一个表达式: {{ 5 + 5 }}</p>
  6. </div>
  7. <script src="//www.w3cschool.cn/try/angularjs/1.2.5/angular.min.js"></script>
  8. </body>
  9. </html>
使用ng-bind
  1. <div ng-app="" ng-init="quantity=1;cost=5">
  2. <p>总价: <span ng-bind="quantity * cost"></span></p>
  3. </div>
AngularJS 数组
  1. <div ng-app="" ng-init="points=[1,15,19,2,40]">
  2. <p>第三个值为 {{ points[2] }}</p>
  3. </div>
ng-repeat 指令会重复一个 HTML 元素:
  1. <div ng-app="" ng-init="names=['Jani','Hege','Kai']">
  2. <p>使用 ng-repeat 来循环数组</p>
  3. <ul>
  4. <li ng-repeat="x in names">
  5. {{ x }}
  6. </li>
  7. </ul>
  8. <div>

如何使用 Scope

  1. <div ng-app="myApp" ng-controller="myCtrl">
  2. <h1>{{carname}}</h1>
  3. </div>
  4. <script>
  5. var app = angular.module('myApp', []);
  6. app.controller('myCtrl', function($scope) {
  7. $scope.carname = "Volvo";
  8. });
  9. </script>

控制器方法

  1. <div ng-app="myApp" ng-controller="personCtrl">
  2. 名: <input type="text" ng-model="firstName"><br>
  3. 姓: <input type="text" ng-model="lastName"><br>
  4. <br>
  5. 姓名: {{fullName()}}
  6. </div>
  7. <script>
  8. var app = angular.module('myApp', []);
  9. app.controller('personCtrl', function($scope) {
  10. $scope.firstName = "John";
  11. $scope.lastName = "Doe";
  12. $scope.fullName = function() {
  13. return $scope.firstName + " " + $scope.lastName;
  14. }
  15. });
  16. </script>

AngularJS 过滤器

AngularJS 过滤器可用于转换数据:

过滤器 描述
currency 格式化数字为货币格式。
filter 从数组项中选择一个子集。
lowercase 格式化字符串为小写。
orderBy 根据某个表达式排列数组。
uppercase 格式化字符串为大写。
orderBy 过滤器根据某个表达式排列数组
  1. <div ng-app="myApp" ng-controller="namesCtrl">
  2. <ul>
  3. <li ng-repeat="x in names | orderBy:'country'">
  4. {{ x.name + ', ' + x.country }}
  5. </li>
  6. </ul>
  7. <div>
$http 是 AngularJS 应用中最常用的服务。 服务向服务器发送请求,应用响应服务器传送过来的数据。(类似jquery ajax,get)
  1. var app = angular.module('myApp', []);
  2. app.controller('myCtrl', function($scope, $http) {
  3. $http.get("welcome.htm").then(function (response) {
  4. $scope.myWelcome = response.data;
  5. });
  6. });
AngularJS $timeout 服务对应了 JS window.setTimeout 函数。
  1. var app = angular.module('myApp', []);
  2. app.controller('myCtrl', function($scope, $timeout) {
  3. $scope.myHeader = "Hello World!";
  4. $timeout(function () {
  5. $scope.myHeader = "How are you today?";
  6. }, 2000);
  7. });
AngularJS $interval 服务对应了 JS window.setInterval 函数。
  1. var app = angular.module('myApp', []);
  2. app.controller('myCtrl', function($scope, $interval) {
  3. $scope.theTime = new Date().toLocaleTimeString();
  4. $interval(function () {
  5. $scope.theTime = new Date().toLocaleTimeString();
  6. }, 1000);
  7. });

创建自定义服务

你可以创建自定义的访问,链接到你的模块中:

创建名为hexafy 的访问:

app.service(‘hexafy’function() {
this.myFunc = function (x) {
return x.toString(16);
}
});

要使用自定义的访问,需要在定义过滤器的时候独立添加:

实例

使用自定义的的服务 hexafy 将一个数字转换为16进制数:

app.controller(‘myCtrl’function($scope, hexafy) {
$scope.hex = hexafy.myFunc(255);
});

使用 ng-options 创建选择框

  1. <div ng-app="myApp" ng-controller="myCtrl">
  2. <select ng-model="selectedName" ng-options="x for x in names">
  3. </select>
  4. </div>
  5. <script>
  6. var app = angular.module('myApp', []);
  7. app.controller('myCtrl', function($scope) {
  8. $scope.names = ["Google", "W3Cschool", "Taobao"];
  9. });
  10. </script>

ng-options 与 ng-repeat

我们也可以使用ng-repeat 指令来创建下拉列表:

  1. <select>
  2. <option ng-repeat="x in names">{{x}}</option>
  3. </select>

数据源为对象

前面实例我们使用了数组作为数据源,以下我们将数据对象作为数据源。

$scope.sites = {
    site01 : "Google",
    site02 : "W3CSchool",
    site03 : "Taobao"
};

ng-options 使用对象有很大的不同,如下所示:

实例

使用对象作为数据源, x 为键(key),y 为值(value):

<select ng-model=“selectedSite” ng-options=x for (x, y) in sites>
</select>

<h1>你选择的值是: {{selectedSite}}</h1>

你选择的值为在 key-value 对中的 value

value 在 key-value 对中也可以是个对象:

实例

选择的值在 key-value 对的 value 中, 这是它是一个对象:

$scope.cars = {
car01 : {brand : “Ford”, model : “Mustang”, color : “red”},
car02 : {brand : “Fiat”, model : “500”, color : “white”},
car03 : {brand : “Volvo”, model : “XC90”, color : “black”}
};

在下拉菜单也可以不使用key-value 对中的 key , 直接使用对象的属性:

  1. <select ng-model="selectedCar" ng-options="y.brand for (x, y) in sites">
  2. </select><div ng-app="" ng-controller="customersController">
  3. <table>
  4. <tr ng-repeat="x in names">
  5. <td>{{ x.Name }}</td>
  6. <td>{{ x.Country }}</td>
  7. </tr>
  8. </table>
  9. </div>
  10. <script>
  11. function customersController($scope,$http) {
  12. $http.get("/statics/demosource/Customers_JSON.php")
  13. .success(function(response) {$scope.names = response;});
  14. }
  15. </script>

ng-disabled 指令

  1. <div ng-app="">
  2. <p>
  3. <button ng-disabled="mySwitch">点我!</button>
  4. </p>
  5. <p>
  6. <input type="checkbox" ng-model="mySwitch">按钮
  7. </p>
  8. </div>
ng-show 指令隐藏或显示一个 HTML 元素。
  1. <div ng-app="">
  2. <p ng-show="true">我是可见的。</p>
  3. <p ng-show="false">我是不可见的。</p>
  4. </div>
ng-click 指令定义了一个 AngularJS 单击事件。
  1. <div ng-app="" ng-controller="myController">
  2. <button ng-click="count = count + 1">点我!</button>
  3. <p>{{ count }}</p>
  4. </div>

隐藏 HTML 元素

ng-hide 指令用于设置应用的一部分 不可见 。

ng-hide=”true” 让 HTML 元素 不可见

ng-hide=”false” 让元素可见。

  1. <div ng-app="" ng-controller="personController">
  2. <button ng-click="toggle()">隐藏/显示</button>
  3. <p ng-hide="myVar">
  4. 名: <input type="text" ng-model="person.firstName"><br>
  5. 姓: <input type="text" ng-model="person.lastName"><br>
  6. <br>
  7. 姓名: {{person.firstName + " " + person.lastName}}
  8. </p>
  9. </div>
  10. <script>
  11. function personController($scope) {
  12. $scope.person = {
  13. firstName: "John",
  14. lastName: "Doe"
  15. };
  16. $scope.myVar = false;
  17. $scope.toggle = function() {
  18. $scope.myVar = !$scope.myVar;
  19. };
  20. }
  21. </script>

AngularJS 包含

使用 AngularJS, 你可以使用 ng-include 指令来包含 HTML 内容:

  1. <body>
  2. <div class="container">
  3. <div ng-include="'myUsers_List.htm'"></div>
  4. <div ng-include="'myUsers_Form.htm'"></div>
  5. </div>
  6. </body>
动画
  1. <body ng-app="ngAnimate">
  2. 隐藏 DIV: <input type="checkbox" ng-model="myCheck">
  3. <div ng-hide="myCheck"></div>
  4. </body>

window下安装sass

1.如果没有安装ruby先到:http://rubyinstaller.org/ 下载安装包安装

2.下载cacert.pem 地址:http://pan.baidu.com/s/1slGS84t

3.将cacert.pem 放置在ruby安装的根目录 如:C:Ruby23-x64

4.新增环境变量 SSL_CERT_FILE=C:Ruby23-x64cacert.pem

5.打开cmd 输入 gem sources -a https://ruby.taobao.org/

6.源添加成功后 输入 gem install sass

这样就安装了 sass 了

Centos系统下修改环境变量PATH路径的方法

比如要把/etc/apache/bin目录添加到PATH中,方法有三:

1.#PATH=$PATH:/etc/apache/bin
使用这种方法,只对当前会话有效,也就是说每当登出或注销系统以后,PATH 设置就会失效

2.#vi /etc/profile
在适当位置添加 PATH=$PATH:/etc/apache/bin (注意:= 即等号两边不能有任何空格)
这种方法最好,除非你手动强制修改PATH的值,否则将不会被改变

3.#vi ~/.bash_profile
修改PATH行,把/etc/apache/bin添加进去
这种方法是针对用户起作用的
注意:想改变PATH,必须重新登陆才能生效,以下方法可以简化工作:

如果修改了/etc/profile,那么编辑结束后执行source profile 或 执行点命令 ./profile,PATH的值就会立即生效了。
这个方法的原理就是再执行一次/etc/profile shell脚本,注意如果用sh /etc/profile是不行的,因为sh是在子shell进程中执行的,即使PATH改变了也不会反应到当前环境中,但是source是在当前 shell进程中执行的,所以我们能看到PATH的改变。

Zeal: 离线 API 文档浏览 + 搜索工具

OSX 上有 Dash, 是个离线的 API 文档搜索 + 浏览工具.

Zeal 是基于 QT 做的一个开源实现. 功能上还很残… 不过非 Mac 用户总归是有得用了.

下载安装

项目主页: http://zealdocs.org/ 官方提供 Windows 和 Ubuntu 的安装包.

Archlinux 用户可以用我的 pkgbuild: zeal-git. 使用中出了问题请告知

其他系统用户只能下载源码并编译: https://github.com/jkozera/zeal#how-to-compile

下载具体文档

刚安装好之后是没有文档可以看的, 需要自己下载. Zeal 兼容 Dash 的 docset 格式, 因此虽然内置下载的界面中选项不多, 但扩充的可能性是有的.

内置的下载界面在这里有截图示意. 不过试了一下 Ruby2 的无法下载. 作者在这里解释说是 Google Drive 的原因. 大家可以自行下载并解压到指定的目录就好了 – – 具体操作方法还是看作者的解释好了.

python 元编程

元编程

黑魔法防御

元编程是一种黑魔法,正派人士都很畏惧。——张教主

何谓元编程

  • 编写一个程序,能够操纵,改变其他程序,这就是元编程
  • 最简单的来说,C的宏就是元编程的一种
  • 元编程的另一大代表则是lisp的宏
  • 虽然不常见,但是汇编级别也是可以元编程的,例如可执行文件压缩
  • 如果源语言和目标语言一样,就是我们最常见的反射

元编程的几种形式

  • 文本宏语言,C宏,M4,Flex,Bison,Gperf
  • S表达式宏,lisp/scheme S表达式的特殊之处在于,他既是数据又是代码,因此S表达式宏可以很轻易的改变代码结构
  • 反射,动态数据结构变更

Python下元编程的几个手段

  • 预定义方法
  • 函数赋值
  • descriptor
  • 元类
  • eval

预定义方法

没啥好多说的,看下面这个例子:

class A(object):

    def __init__(self, o):
        self.__obj__ = o

    def __getattr__(self, name):
        if hasattr(self.__obj__, name):
            return getattr(self.__obj__, name)
        return self.__dict__[name]

    def __iter__(self):
        return self.__obj__.__iter__()

l = []
a = A(l)

for i in xrange(101): a.append(i)

print sum(a)

这是一个再简单不过的agent类,不过不怎么完美。因为__iter__属于预定义函数,不会调用__getattr__来获得。因此还需要额外定义。下面章节中,我们将看到一种简单的多的方法来实现agent类。

另外,提一点细节的差异。__getattr__,__setattr__相对还是比较上层的,至少在这两个函数中,可以访问__dict__。而__getattribute__这个函数中,使用self.__dict__会引发递归,需要用object.__getattribute__(self, name)。相对的,__getattribute__只能用于new style class。

同样,__getattr__,__setattr__,__getattribute__的用法不止于此。通过定义这三个函数,可以对类的成员做出非常多的变化。但是,和下面提到的手段比起来,这无疑是比较初级的。

函数赋值

我们看这个从socket.py中摘出来的例子:

_delegate_methods = ("recv", "recvfrom", "recv_into", "recvfrom_into",
                     "send", "sendto")

def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, _sock=None):
    if _sock is None:
        _sock = _realsocket(family, type, proto)
    self._sock = _sock
    for method in _delegate_methods:
        setattr(self, method, getattr(_sock, method))

当你调用s.recv(4)的时候,你以为自己在调用_socketobject的方法?错了,那方法其实是对应的_realsocket的。这是替换实例函数的例子。


这可以做什么用?我们来看我写的一个http代理装饰器。

def http_proxy(proxyaddr, username=None, password=None):
    def reciver(func):
        def creator(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0):
            sock = func(family, type, proto)
            sock.connect(proxyaddr)
            def newconn(addr): http_connect(sock, addr, username, password)
            sock.connect, sock.connect_ex = newconn, newconn
            return sock
        return creator
    return reciver

我们再看descriptor里面的这个例子:


class A(object):

    def b(self):
        print 'ok'

a = A()
print A.b, a.b
<unbound method A.b> <bound method A.b of <__main__.A object at 0x7f81620d9990>>
print a.b.im_self == a, a.b.im_func == A.b.im_func
True True
print A.__dict__['b'], A.b.im_func
<function b at 0x7f81620db500> <function b at 0x7f81620db500>

def c(self): print 'not ok'
A.b = c

print A.b, a.b
<unbound method A.c> <bound method A.c of <__main__.A object at 0x7f81620d9990>>
print a.b.im_self == a, a.b.im_func == A.b.im_func
True True
print A.__dict__['b'], A.b.im_func
<function c at 0x7f81620db488> <function c at 0x7f81620db488>

a.b()
not ok

这同样是函数替换,不过替换的是类函数方法。

Descriptor

所谓descriptor,就是带有__get__和__set__函数的对象。当访问某个对象的某个属性,这个属性又是一个descriptor时。返回值是descriptor的__get__调用的返回,set同理类推。带有__set__的称为data descriptor,只有__get__的称为non data descriptor。

python访问某个对象的某个属性时,是按照以下次序的:

  1. class的data descriptor。
  2. instance属性,无论其是否是descriptor,不调用__get__。
  3. class属性,包括non data descriptor。

使用descriptor,可以很容易的定义a.name之类获得值和设定的操作中需要执行什么。

实际上,我们使用的类函数就是基于descriptor做的。


class A(object):

    def b(self):
        print 'ok'

a = A()
print A.b, a.b
print a.b.im_self == a, a.b.im_func == A.b.im_func
print A.__dict__['b'], A.b
<function b at 0x7f81620db500> <unbound method A.b>

最后一个A.__dict__[‘b’], A.b,揭示了一个问题,两者不一致。至于为什么?那是因为descriptor在起作用,在A.b的时候,调用了某个__get__,将函数和类组合成和method对象丢了出来。这个__get__在哪里呢?我们来看这么个例子。


def f(self): print self['a'], 'ok'

print dir(f)
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']  
f({'a': 1})
1 ok

o = {'a': 1}
m = f.__get__(o, dict)
print m
<bound method dict.f of {'a': 1}>
m()
1 ok

这可说的不能再明白了,function对象本身就具备__get__,是non data descriptor。按照上述的规则,排在instance之后。所以,我们给instance加载属性,可以重载掉类的函数。

我们看下面这个例子,这同样是从本公司的业务系统中摘出来简化的。


class Meta(type):
    def __new__(cls, name, bases, attrs):
        for k, v in attrs.items():
            if hasattr(v, '__meta_init__'): v.__meta_init__(k)
        return type.__new__(cls, name, bases, attrs)

class AttrBase(object):

    def __meta_init__(self, k): self.name = k
    def __get__(self, obj, cls): return obj[self.name]
    def __set__(self, obj, value): obj[self.name] = value

class Base(dict):
    __metaclass__ = Meta

class User(Base):

    name = AttrBase()

b = User()
b.name = 'shell'
print b
print b.name

注意到,当你访问b.name的时候,实际上是去访问了b[‘name’]。这个过程不是通过User类重载__getattr__实现的,而是通过descriptor。另外,我们处理这个例子的时候,用到了元类。下面一节介绍一下元类。

元类

我们先看这么一个例子:

class Base(dict):
    __metaclass__ = Meta
    def output(self, o): print 'hello, %s' % o
b = Base()
b.output('world')

你认为输出是什么?

再加上下面的代码呢?

class Meta(type):
    def __new__(cls, name, bases, attrs):
        output = attrs['output']
        attrs['output'] = lambda self, x: output(self, 'python')
        return type.__new__(cls, name, bases, attrs)

实际上,输出是hello, python。


为什么?我们要从type说起。在python中,出乎我们的意料,type不是一个函数,而是一个类。type的作用不仅仅可以显示某个对象属于哪个类,更重要的是,type可以动态的创建类。就像下面这样。

A = type('A', (object,), {'b': 1})
a = A()
print A, a.b

我们稍加变化,可以变成这样的代码。没什么区别。

def f(name, bases, attrs):
    attrs['c'] = 2
    return type(name, bases, attrs)

A = f('A', (object,), {'b': 1})
a = A()
print A, a.b, a.c

最后,我们把代码变成这个样子。

def f(name, bases, attrs):
    attrs['c'] = 2
    return type(name, bases, attrs)

class A(object):
    __metaclass__ = f
    b = 1

a = A()
print A, a.b, a.c

__metaclass__实际上,就是指创建类A的时候,要用什么函数进行生成。


可是且慢,type并不是一个函数,而是一个类阿。其实我们不妨这么看,类本身,可以视作是一个构造函数。

class A(object): pass
def B(): return A()

a = A()
b = B()
print a, b

由两者创建出来的对象并没有什么本质区别。所以,以下两个东西,其实在使用上是等价的。

class M(type):
    def __new__(cls, name, bases, attrs):
        attrs['c'] = 2
        return type.__new__(cls, name, bases, attrs)

def f(name, bases, attrs):
    attrs['c'] = 2
    return type(name, bases, attrs)

A = M('A', (object,), {'b': 1})
a = A()
print A, a.b, a.c

既然如此,我们当然可以在__metaclass__中,将f替换为M。

class M(type):
    def __new__(cls, name, bases, attrs):
        attrs['c'] = 2
        return type.__new__(cls, name, bases, attrs)

class A(object):
    __metaclass__ = M
    b = 1

a = A()
print A, a.b, a.c

这就是本文最上面的元类的来历。

我们甚至可以创建元类的元类。

class M1(type):
    def __new__(cls, name, bases, attrs):
        def f(cls, name, bases, attrs):
            attrs['c'] = 2
            return type.__new__(cls, name, bases, attrs)
        attrs['__new__'] = f
        return type.__new__(cls, name, (type,), attrs)

class M2(object):
    __metaclass__ = M1

class A(object):
    __metaclass__ = M2
    b = 1

a = A()
print A, a.b, a.c

当然,大家可能疑惑,为什么舍弃function,而使用元类。function固然简单,但是function是无法继承的。这里不仅仅指我们无法创建一个Meta的子类,扩充meta的行为。而且,使用function的类,一旦继承,其子类是不会管父类的__metaclass__定义的。

Eval

大家看看下面这个程序,谁能看出是干什么的?

exec(compile(__import__('zlib').decompress(
__import__('base64').b64decode('eJylU9Fq4zAQfPdX7FGKpOIqDZQ+BPIVfTlognDs
dSLOlowkN02/vru20jR3HIU7gbG0u5qZ1Ug3PxZjDIuddYvhlA7eFbYffEgQsIR4iiW8d3ZX
wq6K+PRYwh6TH1LRBt+Dj5CLhyodiqLBlmb1L9naDjmkVgXQONp0AD+g+0yUIIJQUEVo7VzD
I8A68+jd0yO62jcomV7Xvh8CxkgAOmDVSKXU36GPGdpfoFuvj8EmlEKImz9axjesJXMQhjRm
bsoYKZhcKN3gp4Cv2Vkr5Uktl5BacRuFUqRB0MewtCJKuIWgiiKg4Ri1GVCf+SjNwc1ZwOb5
jmnpd6GlxUzGkzPZRgqp75TYqM0VI68JVE1+jO5/HGl9gM46BOuu4jxsO6V0TFVIkRFl7ng1
OZmb1X2V6oPkUqX3wY9DlEv18rD9N/+m6/DFj8t9yQ4EvhpT6wfsBpkbHoJ1CcgdeF3qB2Cs
hA52J3imsk7/HNkj5teM6KoeJd1+XYX9K2lVv4G83I9boL7pNXyzr6BjMobjxsB6DcKYvrLO
GDELo8fU2ZhK4B10avP70vPvArVcbelgRjELaalwNpZdEPckngxqbJ1kxlOAXcTpNRbZLMa5
dpYivG9KQCunLvBtqFwzRgyS4vmVMdYqn2fxAdHyTCM=')),'', 'exec'))

看不出来是吧?那先看看这个例子:

def remove_list(li, obj):
    lic = li[:]
    lic.remove(obj)
    return lic

ops = ["+", "-", "*", "/"]
def gen_make(nums, *exes):
    if len(nums) == 0:
        try:
            if eval("".join(exes)) == 24: print "".join(exes).replace(".0", "")
        except: pass
    elif len(exes) == 0:
        for n in nums: gen_make(remove_list(nums, n), str(n) + ".0")
    else:
        if len(exes) > 1:
            exes = list(exes)
            exes.insert(0, '(')
            exes.append(')')
        for n in nums:
            for op in ops:
                gen_make(remove_list(nums, n), str(n) + ".0", op, *exes)

gen_make([3, 4, 6, 8])

这是我写的一个24点计算程序,相对有点取巧。核心是利用字符串拼装表达式,然后用eval看看是不是等于24。相对来说,不使用eval的代码就要复杂很多。当然,下面这个版本要完整很多。

from itertools import combinations

class opt(object):
    def __init__(self, name, func, ex=True):
        self.name, self.func, self.exchangable = name, func, ex
    def __str__(self): return self.name
    def __call__(self, l, r): return self.func(l, r)
    def fmt(self, l, r):
        return '(%s %s %s)' % (fmt_exp(l), str(self), fmt_exp(r))

def eval_exp(e):
    if not isinstance(e, tuple): return e
    try: return e[0](eval_exp(e[1]), eval_exp(e[2]))
    except: return None

def fmt_exp(e): return e[0].fmt(e[1], e[2]) if isinstance(e, tuple) else str(e)
def print_exp(e): print fmt_exp(e), eval_exp(e)

def chkexp(target):
    def do_exp(e):
        if abs(eval_exp(e) - target) < 0.001: print fmt_exp(e), '=', target
    return do_exp

def iter_all_exp(f, ops, ns, e=None):
    if not ns: return f(e)
    for r in set(ns):
        ns.remove(r)
        if e is None: iter_all_exp(f, ops, ns, r)
        else:
            for op in ops:
                iter_all_exp(f, ops, ns, (op, e, r))
                if not op.exchangable:
                    iter_all_exp(f, ops, ns, (op, r, e))
        ns.append(r)

opts = [
    opt('+', lambda x, y: x+y),
    opt('-', lambda x, y: x-y, False),
    opt('*', lambda x, y: x*y),
    opt('/', lambda x, y: float(x)/y, False),]

if __name__ == '__main__':
    iter_all_exp(chkexp(24), opts, [3, 4, 6, 8])

回到最上面的那个表达式,那是一个程序被zip后base64的结果。当然,这个结果字符串被写入一个程序中,程序会自动解开内容进行eval。这种方法能够将任何代码变为一行流。而这个被变换的程序,就是实现这个功能的。

语法合成转换

语法转换的最著名例子是orm,为什么?orm实际上,将python语法转换成了sql语法。

慎用元类

正派人士为什么畏惧黑魔法?因为元编程会破坏直觉。

作为一个受到多年训练的程序员,你应当对你熟练使用的语言有一种直觉。看到dict(zip(title, data))就应当想到,这是一个拼接数据生成字典的代码。看到[(k, v) for k, v in d.iteritems() if k…]就应当知道,这是一个过滤算法。这是在长期使用程序后形成的一种条件反射。

而元编程会很大程度的破坏这种直觉。这也是为什么我很讨厌C++的算符重载的原因。你能够想像么?o = a + b;这个表达式,其实想表达的是两颗特定条件的树的拼和(concat)过程,而非合并(merge)过程。每次使用重载过的系统,我都需要重新训练我的直觉系统。

python的元编程具有同样可怕的效果。还记得eval中那个自压缩的例子么?那是一个极端,将人类可理解的程序编码为了人类无法理解的。而meta的那个例子说明,元编程可以在不知不觉中修改原始的定义。

python中的元编程手段远远不止上述这些,很多时候,我们自己都毫无感觉。甚至,要修改一个行为,不一定需要元编程,重载同样也可以让人摸不着头脑。但是由于元编程的复杂性,用户更难在其中进行源码阅读,跟踪,调试。

在设计,规划这类代码的时候,必须注意。首先,你的设计需要尽量符合直觉,尽量让使用者感到舒服。其次,你需要比常规程序更多的文档,尽量减少用户在阅读源码上的时间——除非你万分的有信心,用户能够毫无障碍的阅读你的源码。最后,你需要比较精细的测试,和更多的,更友好错误处理。因为一旦发生异常,用户可能无法处理不友好的抛出。

ORM的意义和目标

为什么要用ORM

ORM的根本目的,是将关系型数据库模型转换为面对对象模型。此外,他还兼具了一些其他功能。例如:

  • 跨数据库
  • 对象缓存
  • 延迟执行

对象缓存

对象缓存的目的在于减少SQL的执行,增加程序执行速度,减少数据库开销。从某种意义来说,写的好的程序是不需要对象缓存的。但是这个“写的好”对程序设计提出了及其变态的要求。他要求无论程序由多少个组件组成,他们都必须能彼此传递数据,甚至知道对方的存在和细节,这样才能消除无效的查询和提交。但是这一要求使得代码之间产生了紧耦合,不利于系统的扩展。

Lazy Evaluation

lazy evaluation,中文翻译为惰性求值。指的是表达式的执行被延缓到真正需要值的时候。在ORM中,lazy evaluation一般是指查询过程不发生在查询语句生成的时候,而发生在实际发生数据请求的时候。

两者的区别在于,非lazy evaluation需要一次性完成表达式拼装,因此其逻辑是集中式的,不利于模块化。而lazy evaluation则可以将表达式逻辑的拼装分散在各个系统中。这同样是从系统耦合性和扩展性上来的需求。

另一种的lazy evaluation则是,在请求数据的时候只返回数据的一部分,当枚举到后续部分时再继续请求数据。如果情况合适,这个技巧可以有效减少计算开销和网络负载,或者减小响应时间。但是返回片段过小,请求过于频繁,应用场景不正确,反而会降低效率。

REDIS和RDBMS的区别

ACID

ACID是RDBMS的四个基本特性,即:

  • 原子性(Atomicity):一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。
  • 一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性限制没有被破坏。
  • 隔离性(Isolation,又称独立性):当两个或者多个事务并发访问(此处访问指查询和修改的操作)数据库的同一数据时所表现出的相互关系。
  • 持久性(Durability):在事务完成以后,该事务对数据库所作的更改便持久地保存在数据库之中,并且是完全的。

redis的ACID:

  • 原子性:redis本身是原子的,但是redis集群做不到原子性。redis只有一个线程(后台线程不处理实际业务),因此redis线性化处理每个指令。每个指令的处理都是不可打断的,原子化的。对于一系列指令,redis有pipeline。然而,如果使用kv将数据存储分布在多个节点上,那么实际上是无法保证多个节点同时成功或失败的。
  • 一致性:一般而言,在单个节点上,只要写的不是太差,满足原子性的多数都满足一致性。但是当冗余数据或者数据约束跨越多节点时,很容易发生不一致。
  • 隔离性:我们无法让redis满足隔离性,单节点也不行。
  • 持久性:根据配置,redis可能满足持久性。如果打开aof模式,redis的性能会大幅下降,但是此时满足持久性。如果使用dump模式或者干脆用Replication替代,那么显然不满足持久性。

ACID不完整造成的问题

  • redis不支持隔离性,实际上普通数据库中涉及隔离性往往也很晕很绕。因此如果你打算设计一个ORM来完成隔离性,我建议你更换数据库。

    在redis中解决这个问题的唯一方法是引入对象锁,包括全局锁,表锁,或者行级锁。但是这太重了。

  • 如果你的所有查询和写入都不冗余,也没有跨越多个对象的约束,那么多节点不会破坏一致性。

    例如,你在user中保存了用户的权限信息。在session中,为了加速访问,你复制了这一信息。那么,user和session中的数据构成冗余。当某个session的user和他不在同一个节点中的时候,我们无法通过pipeline保证对两者的操作同时完成或者同时失败。所以,可能会破坏一致性。 又例如,你需要限制只能有10个user具备管理员权限。这些user可能分布在多个不同的节点上,同样,我们也无法保证操作时一定不会破坏这个约束。 WAL(预写式日志Write-Ahead Logging)可以有效的解决这个问题,但是在redis这种轻量级业务系统上使用WAL怎么看都太重了。 当然,你可能觉得这没什么大不了。那是因为在这个例子中,两个数据有主次关系,即使一致性被破坏也无所谓。

  • redis中没有关系,因此你需要重新设计关系系统。然而双边关系系统会造成冗余数据,引发一致性问题。

    例如:我们的user对象有parent和children两个属性,是一个自身的一对多关系。有两个对象,user1和user2,刚好分别分布在node1和node2两个节点上。那么,user1和user2的关系建立过程需要同时修改node1和node2。如果不加以特殊的控制,很难保证node1和node2同步完成或失败。

  • redis中没有索引系统,因此无法使用where子句,也做不到unique。不过可以通过一个额外的键追踪数据来做到这点。然而如果你自制了索引系统,那么形成了冗余数据。因此,使用索引会引发一致性问题。

    一个比较好的解决方法是(我没实验过),在服务器端,利用lua来写一些过程,负责数据的查询。

REDISOBJ

Redisobj的对象框架

大家应该猜到了,在基于redis的ORM中,我们主要需要使用元类和descriptor两种元编程方法。下面是使用时的样例代码:

class User(redisobj.Base):
    username = redisobj.String()
    password = redisobj.String()
    priv = redisobj.Integer()
    domain = redisobj.ForeignKey('Domain')

class Domain(redisobj.Base):
    name = redisobj.String()

class UserGroup(redisobj.Base):
    name = redisobj.String()

def run(c):
    d1 = Domain(name='domain0')
    c.save(d1)

    u1 = User(username='user1', priv=1, domain=d1)
    u2 = User(username='user2', priv=1, domain=d1)
    ug1 = UserGroup(name='usergroup1')
    ug2 = UserGroup(name='usergroup2')
    c.save(u1, u2, ug1, ug2)

    c.flush()
    u3 = c.load_by_id(User, 1)
    print u3.copy()

    u3.priv = 2
    u3.password = 'abc'
    del u3.username
    c.save(u3)
    c.flush()
    u4 = c.load_by_id(User, 1)
    print u4.copy()

    c.delete(u2)
    try: c.load_by_id(User, 2)
    except LookupError: print 'yes, User1 disappeared.'
    print c.list(User)

我们分析一下上述代码。User,Domain,UserGroup三者,都是继承自redisobj.Base,而这个类,则是由元类创建的。因此,元类Meta可以轻易的替换其中的属性。我将所有继承自AttrBase的全部归并到一起。具体的DataType,例如Integer,String,都是派生自这个类。于是,Base的所有继承者,都可以用Class.__attrs__访问属性列表。而使用instance.prarmeter_name的时候,descriptor发生作用,读取Base中的具体数据。这大概构成了redisobj的对象框架。

Redisobj的Manager

manager是整个redisobj的核心,所有的保存,加载,都直接和manager打交道。当然,一种更加好看的方法是将manager全局化,然后在Base中添加方法(相应的,子类中需要添加类方法)来进行save/delete等行为。然而这将manager限定为全局只有一个(包括集群)。实际中碰到的很多例子,一个程序需要处理超过一个的redis(或者redis集群)。因此,我们在设计的时候保持了manager和object分离的设计思路。

不完整的ForeignKey

ForeignKey是所有数据类型中最特殊的一个,因为它重载了预定的descriptor。在我们的数据字典中,他和Integer没有区别(在关系上,ForeignKey也是Integer的一个子类)。然而,由于重载__get__和__set__。因此你可以认为obj.fk是一个对象。

注意,这里为什么重载descriptor,而不是直接将对象load入数字字典。因此被load入的对象也可能具备引用。反复引用之下,我们直接load对象的行为可能引发整个数据库被load入缓存的风险。而通过descriptor,我们可以在需要的时候载入对象,从而实现lazy evaluation。

但是这是不完整的行为!注意到redisobj里面只有FK,从来没有说反向引用,关系之类的说法。也就是说,当你在一个对象中保存另一个对象,可没有反向引用自动生成,当然也没有办法找到到底有多少个对象引用了当前对象。诚然,你可以自己做反向引用,然后自行添加。然而其中的不一致性问题需要自行解决。

 

作者:zihua | 转自:http://www.pythontip.com/blog/post/4159/

关于Javascript的元编程

相信大家对元编程多少有些了解,元编程简单说就是“编写代码的代码”,换个高雅解释即是“元编程是编写在运行时操纵语言构件的代码”。

反射用元编程解释就是,一门语言拥有对自身的元编程能力就表现在反射。

Demo短小,所以没多加过多的业务注释,毕竟代码很短,不是想告诉解决某一特殊的模式问题。只是想传达一种编程范式,不必深究其中的业务逻辑。

下面的demo问题模型如下:

    “一个部门有很多例如电脑之类的硬件资源,这些硬件需要IT部门管理,

     每台电脑又由很多零件组成(显卡,声卡,CPU,鼠标,键盘……),

     这个电脑财产的管理软件维护工作在你的手上,不过这个第一版不是你开发的,

     IT部门对你维护的此款管理硬件管理系统提出了一个新的需求,

      要求电脑组件价格超过100刀乐的要在输出前加上‘*’  ”

下面这个DS类是核心的底层接口类。你也可以叫它model层。封装了底层关于电脑的数据库。总之这个是别人给你提供的。你要做的就是在这个封装了数据信息的接口基础上做上层开发。

function DS( computorId ){
    
    this.computorId = computorId;
    //不同的电脑有不同的配置。
    this.data = {
        
        mouseInfo : "鼠标",
        mousePrice : "999",
        keyboardinfo : "键盘",
        keyboardprice : "888",

        lcdInfo : "驱动",

        lcdPrice : "888"

        /*
         *略去其他音响,声卡,先卡等属性
         */
    }
}

//DS 您可以当做数据库系统,或者前端开发过程中你也可以当做后台返回的json数据。 
DS.prototype = {
    constructor : DS,
    get_mouse_info : function() { return this.data.mouseInfo; }, //取回鼠标信息
    get_mouse_price : function() { return this.data.mousePrice; }, //取回鼠标价格
    get_keyboard_info : function() { return this.data.keyboardinfo; },//取回键盘信息

    get_keyboard_price : function() { return this.data.keyboardprice; }//取回键盘信息

    /*
     *还有其他一些关于显示器,音响,声卡等等信息和价格
     */
}

下面让我们看看最直观的方式

function Computer( id, data_source ){
    this.id = id;
    this.data_source = data_source;
}

//没有重构之前的源代码,这种写法中规中矩,初级程序员都会直观想到这种方式。作为大牛的你一定不会这么平庸的写代码
Computer.prototype = {
    mouse : function() {
        var info = this.data_source.get_mouse_info( this.id ),
            price = this.data_source.get_mouse_price( this.id ),
            result = "mouse:" + info + price;
        price >= 100? return "*" + result : return result;
    }
    keyboard : function() {
        var info = this.data_source.get_keyboard_info( this.id ),
            price = this.data_source.get_keyboard_price( this.id ),
            result = "mouse:" + info + price;
        price >= 100? return "*" + result : return result;
    }

    /*

   此处略去其他显示器,音响之类的信息。

   */
}
var zs = new Computer('2834750234', new DS(2342341244));

从上面的代码我们能直观分析出来,这种写法重复性很强,很多重复工作。典型硬编码。电脑有多少设备就需要手动的定义多少种取回设备信息价格的方法。可扩展性和维护性极差。

 

 

//第一次改进-动态派发。

function Computer( id, data_source ) {
    this.id = id; //电脑的Id信息
    this.data_source = data_source; //电脑的组件信息
}
Computer.prototype = {
    mouse : function() {
        return this.component( 'mouse' );
    },
    keyboard : function() {
        return this.component( 'keyboard' ); //动态的调用方法,只需传递方法的字符串参数。
    },

    //电脑其他显示器,音响之类的省略,如果问题模型的组件越多,此处的重复也很多,不过较之第一种,已经优化了许多。
    component : function( name ) {
        var methodName = 'get_' + name,
            info = this.data_source[ methodName + '_info' ]( this.id ), //我们首先要取出关于电脑的一些组件info
            price = this.data_source[ methodName + '_price' ]( this.id ),//其次我们要取出关于电脑的一些price
            result = "mouse:" + info + price;
            reuslt = price >= 100? "*" + result : result; //判断价格,高于100块的加上'*'
        return reuslt;
    }
}

var obj = new DS( '15' );
var comObj = new Computer( 12, obj );
console.log(comObj.keyboard());

 

我们看到较之第一种已经优化了很多,重复性工作也变少了,动态派发的小技巧全在javascript对象方法的动态调用。虽说抽象出通用层,但是还是避免不了硬编码,可维护性也较差。

 

 

 

进一步改进-动态创建方法,对于第二种方法,我们还是无法满足,毕竟重复工作还是占了很大一部分。作为一个大牛的你一定不会写出这种中级程序员的代码,于是你像高级程序员做法发起挑战

var Computer = function() {
    var AimClass = function( id, data_source ) {
        this.id = id;
        this.data_source = data_source;
    }

//第三种写法在于动态的创建方法,动态创建方法?你没听错,就是代码执行中创建方法,这要谢谢我们伟大的new Function(),以前对new Function着实无法理解,谁会这么定义一个function。
    AimClass.define_component = function( name ) {
        var name = name,
            fnBody = 'var methodName = "get_' + name + '",' +
                     'info = this.data_source[ methodName + "_info" ]( this.id ),' +
                     'price = this.data_source[ methodName + "_price" ]( this.id ),' +
                     'result = "mouse:" + info + price;' + 
                     'reuslt = price >= 100? "*" + result : result;' +
                     'return reuslt;'
        this.prototype[ name ] = new Function( 'name', fnBody );//此处是重点,动态创建方法(包括get_*_info,get_*_price);你再也不用手动的去定义那些讨厌的方法了。
        return this;
    }
    AimClass.define_component( 'mouse' ) //不过在此你还是要调用下你的类方法
            .define_component( 'keyboard' );//只需你把方法参数写进去就可以

    //这里还有很多关于显示器,音响之类的。从这里可以看出虽然第三种照第一,第二种优化了很多重复工作。可还是觉得还是需要调用很多这个创建实例方法的类方法
    return AimClass;
}()


var obj = new DS( '15' );
var comObj = new Computer( 12, obj );
console.log(comObj.keyboard());

动态定义方法 相比 动态派发 有了进一步的优化,但是这种优化不是颠覆性的。这里你还需要手动的过程即“AimClass.define_component()”。

 

 

改进之最后一步(内省方式进一步优化代码),终于我们来到终极改造,也是你作为大牛应该一展身手之处

var Computer = function( allMethod ){
    var aimClass = function( id, data_source ) {
        this.id = id;
        this.data_source = data_source;
    }
    AimClass.define_component = function( name ) {
        var name = name,
            fnBody = 'var methodName = "get_' + name + '",' +
                     'info = this.data_source[ methodName + "_info" ]( this.id ),' +
                     'price = this.data_source[ methodName + "_price" ]( this.id ),' +
                     'result = "mouse:" + info + price;' + 
                     'reuslt = price >= 100? "*" + result : result;' +
                     'return reuslt;'
        this.prototype[ name ] = new Function( 'name', fnBody );
    }
    for( var i in allMethod ) { //javascript对象内省机制。这种机制,解放了你的双手。不需要重复工作。不需要重复调用动态创建实例方法的类方法
        var reg = /^get_(.+)_info$/, str = '';
        if ( allMethod.hasOwnProperty( i ) && ( ( typeof allMethod[ i ] ) == 'function' ) && ( i != 'constructor' ) ) {
            str = i.replace( reg, '$1' );
            AimClass.define_component( str );
        }
    }
    return AimClass;
}( DS.prototype ) //把DS的所有方法穿入当参数。
var obj = new DS( '15' );
var comObj = new Computer( 12, obj );
console.log( comObj );
console.log( comObj.keyboard() );

最后的代码我们看到运用点正则的技巧还有对js对象(DS.prototype)内省机制。

 

我们看之间耍的小把戏已经让一个冗余的代码变得可维护性很强。

下面我们来看另外一种奇淫巧计。

由于javascript没有methodmissing这样迷人的内核方法。我自己模拟一个,当然这种模拟是有缺陷的。缺陷就是方法的调用是间接调用。而且模拟的方法不是内核方法。

var AimClass = function( id, data_source ) {
        this.id = id;
        this.data_source = data_source;
}
AimClass.prototype = {
    constructor: AimClass,

    methodmissing: function( name, args ) {
        var methodName = 'get_' + name;
        if ( !this.data_source[ methodName + '_info' ] ) {
            return '找不到此设备信息'
        }
        var info = this.data_source[ methodName + '_info' ]( this.id ),
            price = this.data_source[ methodName + '_price' ]( this.id ),
            result = "mouse:" + info + price;
        reuslt = price >= 100? "*" + result : result;
        console.log(result);
        return this;
    },
    
    methods: function() {
        var args = Array.prototype.slice.call( arguments ),
            methodName = args.shift() || undefined,
            methodArgs = args.length > 1? args : [];

        if ( typeof methodName == 'undefined' ) {
            return;
        } 
        if( this[ methodName ] ) {
            return this[ methodName ].apply( this, methodArgs );
        } else {
            return this[ 'methodmissing' ]( methodName, methodArgs );
        }
    }
}

var b = new AimClass( 12, new DS( '15' ) );

b.methods('keyboard').methods('www');

对象调用方法,例如obj.fn1();说白了过程不过就是向obj对象发送一条‘fn1’的消息,这里我们用b.methods来模拟发消息的过程。

以上代码大部分要做的事情是操作语言构件,而并非直接要处理业务逻辑。让代码去管理代码,好比你直接去管理各代码‘士兵’,不如设立一个代码‘将军’。

本文转自:http://www.cnblogs.com/liuyanlong/archive/2013/05/27/3102161.html