首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python中的扩展类(Monkey Patching)是如何工作的?

Python中的扩展类(Monkey Patching)是如何工作的?
EN

Stack Overflow用户
提问于 2011-06-09 06:23:39
回答 2查看 1.2K关注 0票数 14
代码语言:javascript
复制
class Foo(object):
  pass

foo = Foo()
def bar(self):
  print 'bar'

Foo.bar = bar
foo.bar() #bar

来自JavaScript,如果一个“类”原型增加了某个属性。已知该“类”的所有实例将在其原型链中具有该属性,因此不必对其任何实例或“子类”进行任何修改。

从这个意义上说,像Python这样的基于类的语言如何实现Monkey patching?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-06-09 06:37:49

真正的问题是,怎么可能不是呢?在Python中,类本身就是第一类对象。通过依次查找实例、类和父类上的属性(按方法解析顺序),可以解析对类实例的属性访问。这些查找都是在运行时完成的(就像Python中的一切一样)。如果在创建实例后向类添加属性,实例仍将“看到”新属性,原因很简单。

换句话说,它之所以有效,是因为Python不缓存属性(除非您的代码缓存),因为它不使用负缓存或影子类或任何会抑制它的优化技术(或者,当Python实现这样做时,它们会考虑到类可能会更改),并且因为一切都是运行时的。

票数 11
EN

Stack Overflow用户

发布于 2011-06-09 07:54:35

我刚刚通读了一大堆文档,据我所知,foo.bar是如何解决的,整个故事如下:

  • 我们可以通过以下过程找到foo.__getattribute__吗?如果是,则使用foo.__getattribute__('bar')的结果。
    • (查找__getattribute__不会导致无限递归,但它的实现可能会。)
    • (实际上,我们总是会在新样式的对象中找到__getattribute__,因为object中提供了默认实现-但该实现具有以下过程。;) )
    • (如果我们在Foo中定义一个__getattribute__方法,并访问foo.__getattribute__foo.__getattribute__('__getattribute__') 将被调用!但这并不意味着无限递归--如果你仔细的话;) )

  • 是Python运行时提供的属性的“特殊”名称(例如__dict____class____bases____mro__)吗?如果是这样的话,就使用它。(据我所知,__getattribute__属于这一类,它避免了foo.__dict__字典中的无限recursion.)
  • Is bar?如果是,使用foo.__dict__['bar'].
  • Does foo.__mro__ exist (即,foo实际上是一个类)?如果是这样的话,为foo.__mro__1中的每个基类base执行
  • ::foo.__mro__
  • (请注意,第一个将是foo本身,我们已经在bar中使用了base.__dict__?如果是这样的话:x.__get__(foo, foo.__class__).
  • (Note
  • x是我们发现的(再一次,递归地,但不会造成问题) x.__get__
    • 如果是这样的话,使用base.__dict__['bar'].
    • Can bar本身是一个对象,Python编译器会自动给函数一个__get__属性,这个属性被设计为在这个函数中使用

对于foo.__class__.__mro__的每个基类

  • (请注意,这种递归不是问题:这些属性应该始终存在,并且属于“由foo.__class__.__mro__运行时提供”的情况。foo.__class__.__mro__[0]将始终为foo.__class__,即,在我们的example.)

  • (Note中,即使foo.__mro__存在,我们也要这样做。这是因为类也有一个类:它的名称是type,它提供了最初用于计算__mro__属性的方法。)

  • is bar in base.__dict__?如果是这样的话:x.__get__(foo, foo.__class__).

  • (Note

  • x是我们发现的(再一次,递归地,但不会造成问题) x.__get__
  • 如果是这样的话,使用base.__dict__['bar'].
  • Can bar本身是一个对象,Python编译器会自动给函数一个__get__属性,这个属性被设计为在这个函数中使用

  • 如果我们仍然没有找到可以使用的东西:我们可以通过前面的过程找到foo.__getattr__吗?如果是,则使用foo.__getattr__('bar').
  • If everything failed、raise AttributeError.

的结果

bar.__get__并不是一个真正的函数--它是一个“方法包装器”--但你可以想象它是这样实现的:

代码语言:javascript
复制
# Somewhere in the Python internals
class __method_wrapper(object):
    def __init__(self, func):
        self.func = func
    def __call__(self, obj, cls):
        return lambda *args, **kwargs: func(obj, *args, **kwargs)
        # Except it actually returns a "bound method" object
        # that uses cls for its __repr__
    # and there is a __repr__ for the method_wrapper that I *think*
    # uses the hashcode of the underlying function, rather than of itself,
    # but I'm not sure.

# Automatically done after compiling bar
bar.__get__ = __method_wrapper(bar)

顺便说一句,在自动附加到bar__get__中发生的“绑定”(称为描述符),或多或少就是您必须显式地为self方法指定Python参数的原因。在Javascript中,this本身是神奇的;在Python中,仅仅是将事物绑定到self的过程是神奇的。;)

是的,您可以在自己的对象上显式地设置一个方法,并在您将属性设置为对象的一个实例,然后从另一个类的实例访问它时,让它做一些特殊的事情。Python是极端的反射。:)但是,如果你想学习如何做到这一点,并真正全面地了解情况,你的have a lot阅读to do。;)

票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6286006

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档