定义

首先定义下什么是“占位对象”,以下面这一小段代码为例,

from foo import Foo

def foobar():
    f = Foo()
    f.execute()

比如上述Foo代表了游戏客户端引擎代码,我们希望在服务端运行同一份代码,但服务端没有客户端引擎环境,直接运行代码必然会出现异常。服务端运行代码时并不关心表现层逻辑,只要让引擎相关代码都不发生效用又不抛出异常就可以。因此我们需要一个占位对象,让其替换那些不想关心的类。这样就能够在客户端服务端共享部分代码,而无需重复实现。

Placeholder实现

在明确了目标之后,下面就一步步来实现具体的功能点,从最简单的类原型开始,Placeholder即需要最终实现的占位类。

class Placeholder(object):
    pass

对象构造处理

首先要解决的就是构造问题,让__init__支持变长参数,就能够支持如下任意构造调用方式,

class Placeholder(object):

    def __init__(self, *args, **kwargs):
        super(Placeholder, self).__init__()
Placeholder()
Placeholder('foobar')

函数调用处理

接着来看函数调用处理,为了让调用未定义函数不抛异常,就需要重载__getattribute__。同时为了支持级联调用,需要重载__call__,让Placeholder成为callable对象,

class Placeholder(object):

    ...

    def __call__(self, *args, **kwargs):
        return self

    def __getattribute__(self, name):
        try:
            return object.__getattribute__(self, name)
        except:
            return self
placeholder = Placeholder()
placeholder.foo().bar()

支持迭代访问、下标访问

让Placeholder支持迭代访问,以及in操作,需要处理__iter

class Placeholder(object):

    ...

    def __iter__(self):
        return self

    def next(self):
        raise StopIteration
p = Placeholder()
for i in p:
    print i

逻辑判断处理

Placeholder参与比较判断时,让其永远返回False,

class Placeholder(object):

    ...

    def __nonzero__(self):
        return False

    def __eq__(self, other):
        return False

    def __ne__(self, other):
        return False

    def __lt__(self, other):
        return False

    def __le__(self, other):
        return False

    def __gt__(self, other):
        return False

    def __ge__(self, other):
        return False
p1 = Placeholder()
p2 = Placeholder()
print p1 == p2, p1 != p2, p1 < p2, p1 > p2  # False False False False

数学计算处理

Placeholder尽量避免参与数学计算,毕竟它是为了替换类而非数字。如果要处理也可以针对数学运算、位运算进行一定处理,但计算结果的含义需要注意,

class Placeholder(object):

    ...

    def __neg__(self):
        return self

    def __add__(self, other):
        return self

    def __iadd__(self, other):
        return self

    def __sub__(self, other):
        return self

    def __isub__(self, other):
        return self

    def __mul__(self, other):
        return self

    def __imul__(self, other):
        return self

    def __div__(self, other):
        return self

    def __idiv__(self, other):
        return self

    def __mod__(self, other):
        return self

    def __imod__(self, other):
        return self

    def __divmod__(self, other):
        return self

    def __and__(self, other):
        return self

    def __iand__(self, other):
        return self

    def __or__(self, other):
        return self

    def __ior__(self, other):
        return self

    def __xor__(self, other):
        return self

    def __ixor__(self, other):
        return self

    def __invert__(self):
        return self

    def __lshift__(self, other):
        return self

    def __ilshift__(self, other):
        return self

    def __rshift__(self, other):
        return self

    def __irshift__(self, other):
        return self

将简单数学计算、位运算相关函数全部重载之后,计算问题也可以解决,

p = Placeholder()
print p + 1, p * 1, p / 1, p % 1, p & 1

import处理

import需要分几种情况进行处理,

无子模块的package

对于没有子模块的package,外界import的方式有,

from foo import Foo
import foo

为了替换foo的实现,需要生成对应的一份py文件,在import之后将sys.modules中记录的module替换成Placeholder实例。

# foo/__init__.py

from placeholder import Placeholder
import sys

sys.modules[__name__] = Placeholder()

在import时获取到的就是Placeholder对象,

from foo import Foo
import Foo

含有子模块的package

在from import中会出现级联访问,

from foo.bar import Bar
from foo import Foo

因此不能再像上一种处理方式那样修改sys.modules,否则会出现异常,在生成的替代文件中,需要为每一层子模块创建对应的替代文件,对非模块变量,则在生成py中直接设置其为Placeholder,

# foo/__init__.py

from placeholder import Placeholder
import sys

Foo = Placeholder()

# foo/bar/__init__.py
from placeholder import Placeholder
import sys

sys.modules[__name__] = Placeholder()

替代代码生成需要脚本化。不过这种方式并不完备,处理不了动态加载的代码。

总结

语言的动态特性可以帮助实现一些看着很“奇怪”的需求,借着这个机会又熟悉了下Python的部分特性。