programming oriented opp exercises espaƱol and python oop

python - oriented - Usando property() en classmethods



python object oriented programming pdf (14)

¿Es posible usar la función property () con classmethod decorated functions?

No.

Sin embargo, un método de clase es simplemente un método vinculado (una función parcial) en una clase accesible desde instancias de esa clase.

Como la instancia es una función de la clase y puede derivar la clase de la instancia, puede obtener el comportamiento deseado que desee de una propiedad de clase con property :

class Example(object): _class_property = None @property def class_property(self): return self._class_property @class_property.setter def class_property(self, value): type(self)._class_property = value @class_property.deleter def class_property(self): del type(self)._class_property

Este código se puede usar para probar: debe pasar sin generar ningún error:

ex1 = Example() ex2 = Example() ex1.class_property = None ex2.class_property = ''Example'' assert ex1.class_property is ex2.class_property del ex2.class_property assert not hasattr(ex1, ''class_property'')

Y tenga en cuenta que no necesitamos metaclases en absoluto, y no accede directamente a una metaclase a través de las instancias de sus clases de todos modos.

escribiendo un decorador @classproperty

En realidad, puede crear un decorador de classproperty en unas pocas líneas de código classproperty property (está implementado en C, pero aquí puede ver el Python equivalente):

class classproperty(property): def __get__(self, obj, objtype=None): return super(classproperty, self).__get__(objtype) def __set__(self, obj, value): super(classproperty, self).__set__(type(obj), value) def __delete__(self, obj): super(classproperty, self).__delete__(type(obj))

Luego trate al decorador como si fuera un método de clase combinado con propiedad:

class Foo(object): _bar = 5 @classproperty def bar(cls): """this is the bar attribute - each subclass of Foo gets its own. Lookups should follow the method resolution order. """ return cls._bar @bar.setter def bar(cls, value): cls._bar = value @bar.deleter def bar(cls): del cls._bar

Y este código debería funcionar sin errores:

def main(): f = Foo() print(f.bar) f.bar = 4 print(f.bar) del f.bar try: f.bar except AttributeError: pass else: raise RuntimeError(''f.bar must have worked - inconceivable!'') help(f) # includes the Foo.bar help. f.bar = 5 class Bar(Foo): "a subclass of Foo, nothing more" help(Bar) # includes the Foo.bar help! b = Bar() b.bar = ''baz'' print(b.bar) # prints baz del b.bar print(b.bar) # prints 5 - looked up from Foo! if __name__ == ''__main__'': main()

Pero no estoy seguro de lo bien que esto sería. Un viejo article lista de correo sugiere que no debería funcionar.

Obtener la propiedad para trabajar en la clase:

La desventaja de lo anterior es que la "propiedad de la clase" no es accesible desde la clase, porque simplemente sobrescribiría el descriptor de datos de la clase __dict__ .

Sin embargo, podemos anular esto con una propiedad definida en la metaclase __dict__ . Por ejemplo:

class MetaWithFooClassProperty(type): @property def foo(cls): """The foo property is a function of the class - in this case, the trivial case of the identity function. """ return cls

Y luego, una instancia de clase de la metaclase podría tener una propiedad que acceda a la propiedad de la clase utilizando el principio ya demostrado en las secciones anteriores:

class FooClassProperty(metaclass=MetaWithFooClassProperty): @property def foo(self): """access the class''s property""" return type(self).foo

Y ahora vemos tanto la instancia

>>> FooClassProperty().foo <class ''__main__.FooClassProperty''>

y la clase

>>> FooClassProperty.foo <class ''__main__.FooClassProperty''>

tener acceso a la propiedad de la clase.

Tengo una clase con dos métodos de clase (usando la función classmethod ()) para obtener y establecer lo que es esencialmente una variable estática. Traté de usar la función property () con estos, pero resulta en un error. Pude reproducir el error con lo siguiente en el intérprete:

class Foo(object): _var = 5 @classmethod def getvar(cls): return cls._var @classmethod def setvar(cls, value): cls._var = value var = property(getvar, setvar)

Puedo demostrar los métodos de clase, pero no funcionan como propiedades:

>>> f = Foo() >>> f.getvar() 5 >>> f.setvar(4) >>> f.getvar() 4 >>> f.var Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: ''classmethod'' object is not callable >>> f.var=5 Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: ''classmethod'' object is not callable

¿Es posible usar la función property () con classmethod decorated functions?


Debido a que necesito modificar un atributo que de tal manera sea visto por todas las instancias de una clase, y en el alcance desde el que se invocan estos métodos de clase, no tiene referencias a todas las instancias de la clase.

¿Tiene acceso a al menos una instancia de la clase? Puedo pensar en una forma de hacerlo entonces:

class MyClass (object): __var = None def _set_var (self, value): type (self).__var = value def _get_var (self): return self.__var var = property (_get_var, _set_var) a = MyClass () b = MyClass () a.var = "foo" print b.var


Python 3!

Pregunta anterior, muchas vistas, muy necesitadas de una verdadera forma de Python 3.

Afortunadamente, es fácil con la metaclass kwarg:

class FooProperties(type): @property def var(cls): return cls._var class Foo(object, metaclass=FooProperties): _var = ''FOO!''

Entonces, >>> Foo.var

''¡FOO!''


Al leer las notas de la versión de Python 2.2 , encuentro lo siguiente.

El método get [de una propiedad] no se invocará cuando se acceda a la propiedad como un atributo de clase (Cx) en lugar de como un atributo de instancia (C (). X). Si desea anular la operación __get__ para las propiedades cuando se usa como un atributo de clase, puede subclase la propiedad, es un tipo de estilo nuevo en sí mismo, para extender su método __get__, o puede definir un tipo de descriptor desde cero creando un nuevo clase de estilo que define los métodos __get__, __set__ y __delete__.

NOTA: El método a continuación no funciona para setters, solo getters.

Por lo tanto, creo que la solución prescrita es crear una propiedad de clase como una subclase de propiedad.

class ClassProperty(property): def __get__(self, cls, owner): return self.fget.__get__(None, owner)() class foo(object): _var=5 def getvar(cls): return cls._var getvar=classmethod(getvar) def setvar(cls,value): cls._var=value setvar=classmethod(setvar) var=ClassProperty(getvar,setvar) assert foo.getvar() == 5 foo.setvar(4) assert foo.getvar() == 4 assert foo.var == 4 foo.var = 3 assert foo.var == 3

Sin embargo, los incubadores en realidad no funcionan:

foo.var = 4 assert foo.var == foo._var # raises AssertionError

foo._var se modifica, simplemente sobrescribió la propiedad con un nuevo valor.

También puede usar ClassProperty como decorador:

class foo(object): _var = 5 @ClassProperty @classmethod def var(cls): return cls._var @var.setter @classmethod def var(cls, value): cls._var = value assert foo.var == 5


Aquí está mi sugerencia. No use métodos de clase.

Seriamente.

¿Cuál es la razón para usar métodos de clase en este caso? ¿Por qué no tener un objeto ordinario de una clase ordinaria?

Si simplemente desea cambiar el valor, una propiedad no es realmente muy útil, ¿verdad? Simplemente configure el valor del atributo y termine con él.

Una propiedad solo debe usarse si hay algo que ocultar, algo que podría cambiar en una implementación futura.

Tal vez tu ejemplo sea muy simplificado, y hay un cálculo infernal que has dejado. Pero no parece que la propiedad agregue un valor significativo.

Las técnicas de "privacidad" influenciadas por Java (en Python, nombres de atributos que comienzan con _) no son realmente muy útiles. ¿Privado de quién? El punto de privado es un poco nebuloso cuando tienes la fuente (como lo haces en Python).

Los getters y setters de estilo EJB influenciados por Java (a menudo hechos como propiedades en Python) están ahí para facilitar la introspección primitiva de Java así como para aprobar el compilador de lenguaje estático. Todos esos getters y setters no son tan útiles en Python.


Aquí hay una solución que debería funcionar tanto para el acceso a través de la clase como para el acceso a través de una instancia que usa una metaclase.

In [1]: class ClassPropertyMeta(type): ...: @property ...: def prop(cls): ...: return cls._prop ...: def __new__(cls, name, parents, dct): ...: # This makes overriding __getattr__ and __setattr__ in the class impossible, but should be fixable ...: dct[''__getattr__''] = classmethod(lambda cls, attr: getattr(cls, attr)) ...: dct[''__setattr__''] = classmethod(lambda cls, attr, val: setattr(cls, attr, val)) ...: return super(ClassPropertyMeta, cls).__new__(cls, name, parents, dct) ...: In [2]: class ClassProperty(object): ...: __metaclass__ = ClassPropertyMeta ...: _prop = 42 ...: def __getattr__(self, attr): ...: raise Exception(''Never gets called'') ...: In [3]: ClassProperty.prop Out[3]: 42 In [4]: ClassProperty.prop = 1 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-4-e2e8b423818a> in <module>() ----> 1 ClassProperty.prop = 1 AttributeError: can''t set attribute In [5]: cp = ClassProperty() In [6]: cp.prop Out[6]: 42 In [7]: cp.prop = 1 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-7-e8284a3ee950> in <module>() ----> 1 cp.prop = 1 <ipython-input-1-16b7c320d521> in <lambda>(cls, attr, val) 6 # This makes overriding __getattr__ and __setattr__ in the class impossible, but should be fixable 7 dct[''__getattr__''] = classmethod(lambda cls, attr: getattr(cls, attr)) ----> 8 dct[''__setattr__''] = classmethod(lambda cls, attr, val: setattr(cls, attr, val)) 9 return super(ClassPropertyMeta, cls).__new__(cls, name, parents, dct) AttributeError: can''t set attribute

Esto también funciona con un setter definido en la metaclase.


Después de buscar en diferentes lugares, encontré un método para definir una propiedad de clase válida con Python 2 y 3.

from future.utils import with_metaclass class BuilderMetaClass(type): @property def load_namespaces(self): return (self.__sourcepath__) class BuilderMixin(with_metaclass(BuilderMetaClass, object)): __sourcepath__ = ''sp'' print(BuilderMixin.load_namespaces)

Espero que esto pueda ayudar a alguien :)


Espero que este decorador de @classproperty solo lectura y de solo @classproperty ayude a alguien a buscar propiedades de clase.

class classproperty(object): def __init__(self, fget): self.fget = fget def __get__(self, owner_self, owner_cls): return self.fget(owner_cls) class C(object): @classproperty def x(cls): return 1 assert C.x == 1 assert C().x == 1


Esta no es una respuesta adicional, es solo una extensión de algunos de los ejemplos anteriores para demostrar que el comportamiento ''setter'' no funciona para las propiedades de la clase. Lo escribí para ayudarme a entender lo que estaba pasando y pensé que podría ayudar a alguien más a entender el problema. (Gracias a los autores de muchas de estas respuestas, especialmente @Jason R. Coombs, @Denis Ryzhkov y @Aaron Hall).

# @Aaron Hall''s example class classproperty(property): def __get__(self, obj, objtype=None): return super(classproperty, self).__get__(objtype) def __set__(self, obj, value): super(classproperty, self).__set__(type(obj), value) def __delete__(self, obj): super(classproperty, self).__delete__(type(obj)) class Demo: base_value = 100 via_property_value = None @classmethod def calc_val(cls): return cls.base_value + 1 @classproperty def calc_via_property(cls): if cls.via_property_value: return cls.base_value + cls.via_property_value return cls.base_value @calc_via_property.setter def set_via_property_value(cls, value): # This NEVER gets called cls.via_property_value = value # test it works via a classmethod assert Demo.calc_val() == 101 assert Demo().calc_val() == 101 # test it works via a ''classproperty'' assert Demo.calc_via_property == 100 assert Demo().calc_via_property == 100 # test is works via a ''classproperty'' if the internal value is changed Demo.via_property_value = 100 assert Demo.calc_via_property == 200 assert Demo().calc_via_property == 200 # test it sets the internal value correctly # this actually overrides the property definition instead of the internal value Demo.calc_via_property = 50 assert Demo.via_property_value == 50 # remains unchanged at 100 assert Demo.calc_via_property == 150 # is now the value 50 rather than a function assert Demo().calc_via_property == 150 # is now the value 50 rather than a function


Establecerlo solo en la metaclase no ayuda si desea acceder a la propiedad de la clase a través de un objeto instanciado, en este caso también necesita instalar una propiedad normal en el objeto (que se distribuye a la propiedad de la clase). Creo que lo siguiente es un poco más claro:

#!/usr/bin/python class classproperty(property): def __get__(self, obj, type_): return self.fget.__get__(None, type_)() def __set__(self, obj, value): cls = type(obj) return self.fset.__get__(None, cls)(value) class A (object): _foo = 1 @classproperty @classmethod def foo(cls): return cls._foo @foo.setter @classmethod def foo(cls, value): cls.foo = value a = A() print a.foo b = A() print b.foo b.foo = 5 print a.foo A.foo = 10 print b.foo print A.foo


La mitad de una solución, __set__ en la clase no funciona, aún. La solución es una clase de propiedad personalizada que implementa una propiedad y un método estático

class ClassProperty(object): def __init__(self, fget, fset): self.fget = fget self.fset = fset def __get__(self, instance, owner): return self.fget() def __set__(self, instance, value): self.fset(value) class Foo(object): _bar = 1 def get_bar(): print ''getting'' return Foo._bar def set_bar(value): print ''setting'' Foo._bar = value bar = ClassProperty(get_bar, set_bar) f = Foo() #__get__ works f.bar Foo.bar f.bar = 2 Foo.bar = 3 #__set__ does not


No hay una forma razonable de hacer que este sistema de "propiedad de clase" funcione en Python.

Aquí hay una forma irrazonable de hacerlo funcionar. Sin dudas, puedes hacerlo más fluido con cantidades cada vez mayores de magia de metaclases.

class ClassProperty(object): def __init__(self, getter, setter): self.getter = getter self.setter = setter def __get__(self, cls, owner): return getattr(cls, self.getter)() def __set__(self, cls, value): getattr(cls, self.setter)(value) class MetaFoo(type): var = ClassProperty(''getvar'', ''setvar'') class Foo(object): __metaclass__ = MetaFoo _var = 5 @classmethod def getvar(cls): print "Getting var =", cls._var return cls._var @classmethod def setvar(cls, value): print "Setting var =", value cls._var = value x = Foo.var print "Foo.var = ", x Foo.var = 42 x = Foo.var print "Foo.var = ", x

El problema es que las propiedades son lo que Python llama "descriptores". No hay una manera breve y fácil de explicar cómo funciona este tipo de metaprogramación, así que debo indicarle el descriptor cómo .

Solo necesita comprender este tipo de cosas si está implementando un marco bastante avanzado. Como una persistencia de objeto transparente o un sistema RPC, o un tipo de lenguaje específico de dominio.

Sin embargo, en un comentario a una respuesta anterior, dices que

necesidad de modificar un atributo que de tal manera que sea visto por todas las instancias de una clase, y en el alcance desde el cual se llaman estos métodos de clase no tiene referencias a todas las instancias de la clase.

Me parece que lo que realmente quieres es un patrón de diseño de Observer .


Pruébalo, hace el trabajo sin tener que cambiar / agregar una gran cantidad de código existente.

>>> class foo(object): ... _var = 5 ... def getvar(cls): ... return cls._var ... getvar = classmethod(getvar) ... def setvar(cls, value): ... cls._var = value ... setvar = classmethod(setvar) ... var = property(lambda self: self.getvar(), lambda self, val: self.setvar(val)) ... >>> f = foo() >>> f.var 5 >>> f.var = 3 >>> f.var 3

La función de property necesita dos argumentos callable . dales envolturas lambda (que pasa la instancia como primer argumento) y todo está bien.


Una propiedad se crea en una clase pero afecta una instancia. Entonces, si quiere una propiedad classmethod, cree la propiedad en la metaclase.

>>> class foo(object): _var = 5 class __metaclass__(type): pass @classmethod def getvar(cls): return cls._var @classmethod def setvar(cls, value): cls._var = value >>> foo.__metaclass__.var = property(foo.getvar.im_func, foo.setvar.im_func) >>> foo.var 5 >>> foo.var = 3 >>> foo.var 3

Pero ya que estás usando una metaclase de todos modos, se leerá mejor si mueves los métodos de clase allí.

>>> class foo(object): _var = 5 class __metaclass__(type): @property def var(cls): return cls._var @var.setter def var(cls, value): cls._var = value >>> foo.var 5 >>> foo.var = 3 >>> foo.var 3