2017-02-19 21 views
2

Я смотрел на исходный код для peewee, в частности Model и update функции: https://github.com/coleifer/peewee/blob/a33e8ccbd5b1e49f0a781d38d40eb5e8f344eee5/peewee.py#L4718Как отключить метод class от вызова в экземпляре?

мне не нравится тот факт, что этот метод может быть вызван из экземпляра строки, когда любая операция обновления затрагивает каждую строку в модели, если утверждение неверно связано с предложением where. Таким образом, я хочу найти способ запретить вызов этого класса методов из экземпляров модели.

Некоторые поисковые запросы заставляют меня поверить, что это может быть довольно сложно. delattr от __init__, похоже, не работает. И запуск isclass(self) из функции uppdate всегда возвращает True, так как появляется, что когда мы находимся внутри класса, мы фактически - это класс, а не экземпляр.

Любые предложения?

+0

Я не думаю, что вы можете сделать это с помощью «classmethod'. Вам придется написать свой собственный дескриптор, похожий на 'classmethod', который проверяет, привязан ли он к экземпляру и генерирует исключение. – BrenBarn

ответ

1

Вы можете настроить класс __getattribute__, как в ответе Schwobaseggl, но вы также можете использовать пользовательский метакласс.

Когда мы упоминаем «метакласс» в Python, обычно рассматривается переопределение его метода __new__ и выполнение сложных задач во время создания класса (в отличие от времени создания экземпляра). Однако, если вы оставите все специальные dunder (__these__ __methods__) в стороне, метаклас - это просто класс класса, и все его методы будут видны из самого класса, но не будут видны из экземпляров класса. Это означает, что они не будут отображаться, если один экземпляр «dir», но будет отображаться, когда один «dir» класс - и не будет непосредственно извлечен через экземпляр. (Хотя, конечно, всегда можно сделать self.__class__.method)

Кроме того, несмотря на обоснованную обливание известности metaclasse сложности, перекрывая __getattribute__ сам может иметь некоторые pitfalls.

В данном конкретном случае classs вы хотите защитить alreayd использовать метакласс - но это конкретное использование, в отличие от «обычного» метакласс использует, может быть свободно компонуем так же, как обычные иерархии классов:

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? :-) 

     ... 

(Это должно быть очевидно, но воспринимает не нужно объявлять метод в метаклассе, как методы класс: все они являются для методы класса метакласса например, что класс)

И быстрые демо на консоль:

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' 
+0

Теперь я получаю 'TypeError: конфликт метакласса: метакласс производного класса должен быть (нестрогим) подклассом метаклассов всех его оснований'. –

+0

из этой строки 'return super (BaseModel, cls) .__ new __ (cls, name, bases, attrs)' in 'BaseModel .__ new__' –

0

Вы можете переопределить __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 

Проверка classmethod берется из этого answer по Martijn Pieters.

 Смежные вопросы

  • Нет связанных вопросов^_^