如何禁止在实例上调用类方法?
How do I disallow a classmethod from being called on an instance?
我一直在寻找 peewee
的源代码,特别是 Model
和 update
函数:https://github.com/coleifer/peewee/blob/a33e8ccbd5b1e49f0a781d38d40eb5e8f344eee5/peewee.py#L4718
我不喜欢可以从行实例调用此方法这一事实,如果语句未与 where
子句正确耦合,任何更新操作都会影响模型中的每一行。因此,我想找到一些方法来禁止从模型实例中调用此 class 方法。
谷歌搜索让我相信这可能相当困难。 __init__
中的 delattr
似乎不起作用。更新函数中的 运行 isclass(self)
总是 returns True 因为看起来当我们在 class 方法中时我们实际上 是 class 而不是实例。
有什么建议吗?
您可以重写 __getattribute__
,它为 every 属性访问和 only 实例调用,并检查是被归还为类方法论。或者,您可以拒绝某个 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 by Martijn Pieters。
您可以按照 Schwobaseggl 的回答自定义 class __getattribute__
- 但您也可以使用自定义元 class.
当我们在Python中提到"metaclass"时,通常会想到重写它的__new__
方法并在class创建时做一些复杂的事情(与实例创建相对)时间)。然而,如果你把所有特殊的 dunder (__these__ __methods__
) 放在一边,一个 metaclas 只是一个 class 的 class - 它的所有方法将从 class 本身可见, 但在 class 的实例中不可见。这意味着,它们不会在 "dir" 个实例时出现,但会在 "dir" 个 class 时显示 - 并且无法通过实例直接检索。
(虽然,当然,总是可以做到 self.__class__.method
)
此外,尽管 metaclasse 具有合理的 bad-fame 复杂性,但覆盖 __getattribute__
本身可以有一些 pitfalls。
在这种特定情况下,您要保护的 classs 已经使用元class - 但这种特殊用途与 "ordinary" 元class 使用不同,可以像普通的 class 层次结构一样自由组合:
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? :-)
...
(这应该是显而易见的,但认为您不需要声明
元class中的方法作为class方法:全部是
class元class实例的方法,即class)
以及在控制台的快速演示:
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'
我一直在寻找 peewee
的源代码,特别是 Model
和 update
函数:https://github.com/coleifer/peewee/blob/a33e8ccbd5b1e49f0a781d38d40eb5e8f344eee5/peewee.py#L4718
我不喜欢可以从行实例调用此方法这一事实,如果语句未与 where
子句正确耦合,任何更新操作都会影响模型中的每一行。因此,我想找到一些方法来禁止从模型实例中调用此 class 方法。
谷歌搜索让我相信这可能相当困难。 __init__
中的 delattr
似乎不起作用。更新函数中的 运行 isclass(self)
总是 returns True 因为看起来当我们在 class 方法中时我们实际上 是 class 而不是实例。
有什么建议吗?
您可以重写 __getattribute__
,它为 every 属性访问和 only 实例调用,并检查是被归还为类方法论。或者,您可以拒绝某个 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 by Martijn Pieters。
您可以按照 Schwobaseggl 的回答自定义 class __getattribute__
- 但您也可以使用自定义元 class.
当我们在Python中提到"metaclass"时,通常会想到重写它的__new__
方法并在class创建时做一些复杂的事情(与实例创建相对)时间)。然而,如果你把所有特殊的 dunder (__these__ __methods__
) 放在一边,一个 metaclas 只是一个 class 的 class - 它的所有方法将从 class 本身可见, 但在 class 的实例中不可见。这意味着,它们不会在 "dir" 个实例时出现,但会在 "dir" 个 class 时显示 - 并且无法通过实例直接检索。
(虽然,当然,总是可以做到 self.__class__.method
)
此外,尽管 metaclasse 具有合理的 bad-fame 复杂性,但覆盖 __getattribute__
本身可以有一些 pitfalls。
在这种特定情况下,您要保护的 classs 已经使用元class - 但这种特殊用途与 "ordinary" 元class 使用不同,可以像普通的 class 层次结构一样自由组合:
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? :-)
...
(这应该是显而易见的,但认为您不需要声明 元class中的方法作为class方法:全部是 class元class实例的方法,即class)
以及在控制台的快速演示:
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'