2013-12-20 4 views
-2

Я искал способ определения свойств класса в Python.Как правильно определить свойства класса в Python

Ожидаемое поведение было бы что-то интуитивно, как:

class A: 
    _access_count = 0 
    @classproperty 
    def value1(cls): 
     cls._access_count += 1 
     return 1 

A.value1  # return 1 
A().value1  # return 1 
A._access_count # return 2 
A.value1 = 2 # raise an AttributeError 

Я нашел родственные вопросы на SO, но ни один из них не предлагают эту точную функцию.

This thread имеет приятный пример метакласса, хотя в этом случае он действительно не применяется. Принятый ответ this one предлагает близкое решение, но не обрабатывает mecanism setter.

ответ

0

Поскольку это ok to answer his own question Я напишу то, что я придумал до сих пор.

class classproperty(property): 
    """Class property works exactly like property.""" 
    pass 

def create_instance_property(cls_prop): 
    """Create instance property from class property.""" 
    fget, fset, fdel = None, None, None 
    if cls_prop.fget is not None : 
     fget = lambda self: cls_prop.fget(type(self)) 
    if cls_prop.fset is not None : 
     fset = lambda self, value: cls_prop.fset(type(self), value) 
    if cls_prop.fdel is not None : 
     fdel = lambda self: cls_prop.fdel(type(self)) 
    return property(fget, fset, fdel, cls_prop.__doc__) 

def init_for_metaclass(cls, name, bases, dct): 
    """__init__ method for a metaclass to handle class properties.""" 
    super(type(cls), cls).__init__(name, bases, dct) 
    for key, prop in dct.items(): 
     if isinstance(prop, classproperty): 
      setattr(cls, key, create_instance_property(prop)) 
      setattr(type(cls), key, prop) 

def handle_class_property(cls): 
    """Class decorator to handle class properties.""" 
    name = type(cls).__name__ + "_for_" + cls.__name__ 
    metacls = type(name, (type(cls),), {"__init__": init_for_metaclass}) 
    return metacls(cls.__name__, cls.__bases__, dict(cls.__dict__)) 

До сих пор она работает так же, как и следовало ожидать, даже в случаях наследования:

@handle_class_property 
class A(object): 
    _access_count = 0 

    @classproperty 
    def value1(cls): 
     print cls 
     cls._access_count += 1 
     return 1 

class B(A): 
    _access_count = 0 

    @classproperty 
    def value2(cls): 
     cls._access_count += 1 
     return 2 
    @value2.setter 
    def value2(cls, value): 
     print(value) 


    a = A() 
    b = B() 
    assert (a.value1, A.value1) == (1,1) 
    assert (b.value1, B.value1) == (1,1) 
    assert (b.value2, B.value2) == (2,2) 
    assert B._access_count == 4 
    assert A._access_count == 2 
    B.value2 = 42 # This should print '42' 
    try: B.value1 = 42 # This should raise an exception 
    except AttributeError as e: print(repr(e)) 
Смежные вопросы