2011-07-14 16 views
9

El código es como abajo, sólo la estructura básica:Eliminación de métodos específicos de la clase hija que se heredan de la clase padre

class FooType(type): 
    def __new__(cls, name, bases, classdict): 
     instance = type.__new__(cls, name, bases, classdict) 
     # What can I do here? 
     return instance 

class FooBase(object, metaclass=FooType): 
    def __init__(self): 
     pass 

class Foo(FooBase): 
    def __init__(self, name): 
     self.name = name 

    def method1(self): 
     pass 

    def method2(self): 
     pass 

    def specialmethod(self): 
     pass 

class A(Foo): 
    pass 

class B(Foo): 
    pass 

class C(Foo): 
    _disallowed_methods = ['specialmethod'] 

Lo que quiero hacer es que las instancias de la clase C no deberían tener specialmethod, pero ese método debería estar disponible para las instancias A y B.

Puedo anular este método en la clase C y generar un error, pero preferiría no hacerlo.

que se dan cuenta de que puedo añadir en el código para comprobar _disallowed_methods en el FooType y sobre la base de que el registro si el instance tiene cualquiera de ellos en la salida de dir(instance). Pero parece que no puedo eliminar el método de __dict__ de C usando los métodos que he probado hasta ahora. Los métodos que probé son delattr(instance, 'specialmethod') y del instance.__dict__['specialmethod'].

Los delattr método da como resultado "AttributeError: specialmethod", y los del método da como resultado "TypeError: objeto 'dict_proxy' no admite la eliminación elemento"

Básicamente muchas clases diferentes heredarán de Foo, pero algunas de las ellos no deberían tener métodos específicos disponibles para ellos como C que no deberían tener specialmethod disponible.

¿Qué estoy haciendo mal? ¿O cómo más puedo lograr esto?

+2

Por cierto: Esto violaría la [principio de sustitución de liskov] (http://en.wikipedia.org/wiki/Liskov_substitution_principle). – pillmuncher

Respuesta

1

Or how else can I accomplish this?

Para obtener resultados similares puede usar multiple inheritance.

Mueva los métodos que le gustaría que solo algunos de los hijos tengan desde Foo hasta ExtraFoo. Luego use class A(Foo, ExtraFoo) o class C(Foo). De esta manera, incluso podría "volver a conectar" un método dado más abajo en la jerarquía de los niños.

Si volver a colocar el método no es algo que le interesa a, entonces se podría simplemente tienen ExtraFoo como un hijo de Foo (así: la adición de los métodos, no separándolas) y tienen class A(ExtraFoo) y class C(Foo).

+0

me di cuenta que podía esto creando la clase y subclase adicional utilizando tanto '' foo' y FooExtra' a las clases que necesitan la '' specialmethod' y justo foo' a los otros. Pensé que quizás podría eliminar el método de dict cuando la instancia se está creando con la metaclase. Supongo que estaba equivocado ... – skulled

+1

@skulled - Python es muy flexible, y hay una buena probabilidad de que usted podría conseguir que de alguna manera jugar con los elementos internos. Sin embargo, el punto más importante es que sería un mal diseño. Uno de los principios de OOP es que una subclase "hereda" de su padre, por lo que "deslegitimar" una subclase sería como construir alas en un plano diseñado ** para no ** hacerlo volar. :) Por cierto: debe votar las preguntas que le parecieron útiles, y - si alguna resolvió su problema - también podría marcarlo como "aceptado" (Solo mencionando que noté que usted es nuevo en el sitio, perdóneme si ya lo sabía ese). – mac

+0

¡Gracias! Me di cuenta de que tal vez estaba tomando el enfoque equivocado una vez que las respuestas confirmaron mis sospechas. – skulled

6

Bueno, no puede lograr esto de tal manera, ya que no tiene que modificar la clase C, sino la clase Foo, que realmente contiene specialmethod. Pero en realidad no puede hacerlo ya que class es el objeto mutable global y cualquier cambio a Foo afectará a todas las clases secundarias.

Trate de pensar de otra manera. P.ej. puede modificar la lógica de atributos de acceso C clase:

class C(Foo): 
    def __getattribute__(self, name): 
     if name in ['specialmethod']: 
      raise AttributeError('no such method') 
     return super(C, self).__getattribute__(name) 

Después de eso C('a').specialmethod() produce una retraza:

Traceback (most recent call last): 
    File "meta.py", line 37, in <module> 
    C('a').specialmethod() 
    File "meta.py", line 34, in __getattribute__ 
    raise AttributeError('no such method') 
AttributeError: no such method 
+0

Probé esto (disculpas por no mencionar eso en mi publicación inicial), y planeo seguir esta ruta como último recurso. Desde mi punto de vista, este enfoque significa que cada vez que se utiliza un atributo de instancias de la clase 'C ', pasa por este método y, por lo tanto, mi aprehensión fue su efecto sobre la velocidad/el rendimiento. ¿Es eso lo suficientemente insignificante como para no tener que preocuparme por eso? – skulled

+0

@skulled Sí, podría afectar el rendimiento. Pero no puedo creer que sea significativo en tu caso. Si te estás preocupando solo 'timeit'. –

0

Si usted tiene un padre, que no desea ser modificado, y una hijo con uno o más métodos heredados que desea que no sea accesible, puede hacerlo con descriptores.Uno de el enfoque más simple es utilizar property incorporada:

class Parent: 
    def good_method(self): 
     print('Good one') 

    def bad_method(self): 
     print('Bad one') 

class Child(Parent): 
    bad_method = property(doc='(!) Disallowed inherited') 

one = Parent() 
one.good_method() # > 'Good one' 
one.bad_method() # > 'Bad one' 

two = Child() 
two.good_method() # > 'Good one' 
two.bad_method() # > AttributeError: unreadable attribute 
two.bad_method  # > AttributeError: unreadable attribute 
two.bad_method = 'Test' # > AttributeError: can't set attribute 

Cómo ayuda (dos) lo imprime:

class Child(Parent) 
| Method resolution order: 
|  Child 
|  Parent 
|  builtins.object 
| 
| Data descriptors defined here: 
| 
| bad_method 
|  (!) Disallowed inherited 
| 
| ---------------------------------------------------------------------- 
| Methods inherited from Parent: 
| 
| good_method(self) 
| 
| ---------------------------------------------------------------------- 
| Data descriptors inherited from Parent: 
| 
| __dict__ 
|  dictionary for instance variables (if defined) 
| 
| __weakref__ 
|  list of weak references to the object (if defined) 

Bastante bueno, en mi opinión. Pero se debe tener cuidado de no definir los métodos heredados de esta manera si otros métodos se basan en ellos (esto podría evitarse mediante el uso de clase de proxy, que hereda de los padres y redefine tales métodos a utilizar super().bad_method() en lugar de sólo self.bad_method() y señala la bad_method sí a no permitir descriptor). Podría codificar una lógica de descriptor más complicada si fuera necesario

0

He trabajado con pruebas y tropecé exactamente con el mismo problema.

que ha encontrado un único camino verdadero para eliminar innecesarias 'métodos' de la clase heredada: eliminarlo de los padres. (Esta es una mala idea, ya que romperá TODAS las instancias de todas las clases padre y todas las instancias de todas las clases heredadas si se llama a esa función al menos una vez).

código Ejemplo:

class Base(object): 
    def excessive(self): 
     pass 

class Inher(Base): 
    def __init__(self): 
     #import pdb 
     #pdb.set_trace() 
     del Base.excessive 

b=Base() 
example = Inher() 
try: 
    example.excessive() 
except AttributeError: 
    print("Removed!") 
else: 
    raise Exception("Not removed!") 
try: 
    b.excessive() 
except AttributeError: 
    print("Unfortunately, all instances of Base no longer have .excessive() method") 

La razón es que los métodos 'heredadas' no se almacenan en padre (como código o como enlaces), pero se mantienen dentro de los padres. Cuando alguien llama a un método, Python viaja a través de todas las clases de padres hasta que encuentra uno o se detiene.

En mi caso, pude utilizar esta técnica porque utilicé otras pruebas para mis propósitos y mantuve sus métodos 'setUp/tearDown' y axilar, pero eliminé todas las pruebas.

Cualquier aplicación en la vida real no debe utilizar esta técnica.

Cuestiones relacionadas