__getattr__ para variables estáticas/de clase en python
class-method (5)
(Sé que esta es una pregunta antigua, pero como todas las demás respuestas usan una metaclase ...)
Puede usar el siguiente descriptor de classproperty
simple:
class classproperty(object):
""" @classmethod+@property """
def __init__(self, f):
self.f = classmethod(f)
def __get__(self, *a):
return self.f.__get__(*a)()
Utilízalo como:
class MyClass(object):
@classproperty
def Foo(cls):
do_something()
return 1
@classproperty
def Bar(cls):
do_something_else()
return 2
Tengo una clase como:
class MyClass:
Foo = 1
Bar = 2
Siempre que se invoca MyClass.Foo
o MyClass.Bar
, necesito que se invoque un método personalizado antes de que se devuelva el valor. ¿Es posible en Python? Sé que es posible si creo una instancia de la clase y puedo definir mi propio método __getattr__
. Pero mi scnenario implica usar esta clase como tal sin crear ninguna instancia de ella.
También necesito que se invoque un método __str__
personalizado cuando se invoca str(MyClass.Foo)
. ¿Python proporciona tal opción?
Dependiendo del caso utilizo este patrón.
class _TheRealClass:
def __getattr__(self, attr):
pass
LooksLikeAClass = _TheRealClass()
Luego lo importas y lo usas.
from foo import LooksLikeAClass
LooksLikeAClass.some_attribute
Esto evita el uso de metaclase, y maneja algunos casos de uso.
Nadie sorprendido señaló este:
class FooType(type):
@property
def Foo(cls):
return "foo!"
@property
def Bar(cls):
return "bar!"
class MyClass(metaclass=FooType):
pass
Trabajos:
>>> MyClass.Foo
''foo!''
>>> MyClass.Bar
''bar!''
(para Python 2.x, cambie la definición de MyClass
a:
class MyClass(object):
__metaclass__ = FooType
)
Lo que dicen las otras respuestas sobre str
es cierto para esta solución: debe implementarse en el tipo que se devuelve.
Para el primero, deberá crear una metaclase y definir __getattr__()
en eso.
class MyMetaclass(type):
def __getattr__(self, name):
return ''%s result'' % name
class MyClass(object):
__metaclass__ = MyMetaclass
print MyClass.Foo
Para el segundo, no. Al llamar a str(MyClass.Foo)
invoca MyClass.Foo.__str__()
, por lo que deberá devolver un tipo apropiado para MyClass.Foo
.
__getattr__()
y __str__()
para un objeto se encuentran en su clase, por lo que si desea personalizar esas cosas para una clase, necesita la clase de una clase. Una metaclase.
class FooType(type):
def _foo_func(cls):
return ''foo!''
def _bar_func(cls):
return ''bar!''
def __getattr__(cls, key):
if key == ''Foo'':
return cls._foo_func()
elif key == ''Bar'':
return cls._bar_func()
raise AttributeError(key)
def __str__(cls):
return ''custom str for %s'' % (cls.__name__,)
class MyClass:
__metaclass__ = FooType
# in python 3:
# class MyClass(metaclass=FooType):
# pass
print MyClass.Foo
print MyClass.Bar
print str(MyClass)
impresión:
foo!
bar!
custom str for MyClass
Y no, un objeto no puede interceptar una solicitud para una cadena de uno de sus atributos. El objeto devuelto para el atributo debe definir su propio __str__()
.