Ruby可以直接打开一个已定义的类(模块),打开与定义与其他语句没有本质区别,第二次使用class
关键字之后,之后的语句就是进入这个类的封闭作用域内进行一些操作。
在Python中,类只允许一次有效定义,每使用一次class
关键字,都作为一个独立的定义类操作。
在Python里面,需要定义或修改方法、属性不需要专门的“打开”,在专门的作用域里面操作。
def hi():
return 'hi'
class Klass:
pass
Klass.attr1 = 1
klass.method1 = hi
但每次先定义一个函数然后再“赋值”的代码感觉不够优美,这里可以通过装饰器,即定义一个未绑定函数,然后返回装饰后的“绑定”函数。方法参考:在 Python 中实现 Ruby 的 Open Class 和特异方法。
Python的面向对象特征是建立在基于函数的环境之上的。Class的方法以函数的方式定义在类中,当访问一个方法的时候:
class Klass: pass
Klass.a_method # => Klass.__dict__[a_method].__get__(Klass) => unbound function in py2k, raw function in py3k
instance = Klass()
instance.a_method # => type(instance).__dict__[a_method].__get__(instance, type(instance)) => bound function
上面的例子说明了,访问一个类方法的时候,实际上是在当前类(实例的元类)的属性字典中根据方法名查找到对应方法(不存在则调用__getattr__
方法),然后调用函数的__get__
方法(函数也是对象),这个方法的两个参数实际上决定了目标函数是一个普通函数、静态方法、类方法还是实例方法等,以及执行的上下文实体,意思就是描述符在你要调用一个方法的时候将obj.f(*args)
转换成了f(obj, *args)
,Klass.m(*args)
转换成了m(*args)
。
所以,在类中新增方法有一种写法,在实例中新增绑定方法有三种写法(可以理解为将实例绑定到函数对象):
- 偏函数
- 描述符
- Types声明
from functools import partial
from types import MethodType
def attach_method(target):
if isinstance(target, type):
# 类
def decorator(func):
setattr(target, func.__name__, func)
else:
# 实例
def decorator(func):
# bound_func = partial(func, target) # partial function
# bound_func = func.__get__(target) # descriptor
bound_func = MethodType(func, target) # types
setattr(target, func.__name__, bound_func)
return decorator
或者说再写一个装饰器,装饰新定义的类,将新类的属性和方法attach到原同名类内,达到类似“打开”类操作的效果,但只能重定义,而不能够真正使用原类中的变量或方法 。但是,实例写一个类似的instance_eval
的话,只能装饰在def
或者class
关键字上,使用上并不直观。
def class_eval(obj):
original_obj = globals().get(obj.__name__)
for k, v in obj.__dict__.items():
if k not in ['__dict__', '__weakref__']:
attach_method(original_obj)(v, k)
return original_obj
@class_eval
class Klass: # same class name
new_attr: 1
def hi(self):
return "Hello"
以上就是绑定函数或属性到对象(类)。