如何禁止在实例上调用类方法?

时间:2017-02-19 02:18:17

标签: python class-method peewee cpython

我一直在寻找peewee的源代码,特别是Modelupdate函数:https://github.com/coleifer/peewee/blob/a33e8ccbd5b1e49f0a781d38d40eb5e8f344eee5/peewee.py#L4718

如果语句没有与where子句正确耦合,当任何更新操作影响模型中的每一行时,我不喜欢这种方法可以从行实例调用。因此,我想找到一些方法来禁止从模型实例中调用这个类方法。

有些谷歌搜索让我相信这可能会非常困难。来自delattr的{​​{1}}似乎无效。从uppdate函数运行__init__总是返回True,因为当我们在classmethod中时,我们实际上类,而不是实例。

有什么建议吗?

2 个答案:

答案 0 :(得分:1)

您可以像Schwobaseggl的答案一样自定义课程__getattribute__ - 但您也可以使用自定义元类。

当我们提及"元类"在Python中,人们通常认为覆盖其__new__方法并在类创建时执行复杂的操作(与实例创建时间相反)。但是,如果你把所有特殊的dunder(__these__ __methods__)放在一边,那么metaclas只是一个类的类 - 它的所有方法都可以从类中看到,但是从班级的实例。这意味着,当一个" dir"是一个实例时,他们不会出现,但是当一个" dir"类 - 并且不能通过实例直接检索。 (当然,人们总是可以做self.__class__.method

此外,尽管metaclasse的复杂性声名狼借,但压倒__getattribute__本身可能会有一些pitfalls

在这个特定的情况下,你想要保护的类使用元类 - 但这个特殊用途,不像"普通"元类使用,可以像普通的类层次结构一样自由组合:

class ClsMethods(BaseModel):  
     # inherit from `type` if there is no metaclass already

     # now, just leave __new__, __init__, __prepare__ , alone
     # and write your class methods as ordinary methods:
     def update(cls, *args, **kw):
          ...

     def fetch_rows_from(self, ...):
          ...

class Model(with_metaclass(ClsMethods)):
      # This really socks. Do you really still need Py2 support? :-) 

      ...

(显而易见,但认为你不需要申报 元类中的方法为classmethods:所有方法都是 metaclass实例的classmethods,即类)

在控制台上进行快速演示:

In [37]: class M(type):
    ...:     def secret(cls): print("At class only")
    ...:     

In [38]: class A(metaclass=M):
    ...:     pass
    ...: 

In [39]: A.secret()
At class only

In [40]: A().secret()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-40-06355f714f97> in <module>()
----> 1 A().secret()

AttributeError: 'A' object has no attribute 'secret'

答案 1 :(得分:0)

您可以覆盖为{strong>每次属性访问而调用的__getattribute__,并为实例,并检查为classmethodicity返回的内容。或者,您可以拒绝某个item

import inspect

class A(object):  # aka Model
    @classmethod
    def f(cls, *args, **kwargs):
        print(args, kwargs)

class B(A):  # your Model subclass
    def __getattribute__(self, item):
        # if item == 'update':
        #     raise TypeError
        obj = super(B, self).__getattribute__(item)
        # classmethod check
        if inspect.ismethod(obj) and obj.__self__ is B:
            raise TypeError
        return obj

> a = A()
> b = B()

> A.f(5, p=7)
(5,) {'p': 7}

> B.f(5, p=7)
(5,) {'p': 7}

> a.f(5, p=7)
(5,) {'p': 7}

> b.f(5, p=7)
# TypeError

类方法检查来自此answer Martijn Pieters