round objects functions español classes and python python-3.x subclass built-in-types

objects - Subclasificación de tipos incorporados en Python 2 y Python 3



python classes functions (3)

Como seguimiento a la respuesta de Sven, aquí hay una solución de envoltura universal que se ocupa de todos los métodos no especiales. La idea es capturar la primera búsqueda que proviene de una llamada de método e instalar un método de envoltura que realice la conversión de tipo. En búsquedas posteriores, el envoltorio se devuelve directamente.

Advertencias:

1) Esto es más truco de magia que me gusta tener en mi código.

2) Todavía necesitaría ajustar métodos especiales ( __and__ etc.) manualmente porque su búsqueda pasa __getattribute__ alto __getattribute__

import types class MySet(set): def __getattribute__(self, name): attr = super(MySet, self).__getattribute__(name) if isinstance(attr, types.BuiltinMethodType): def wrapper(self, *args, **kwargs): result = attr(self, *args, **kwargs) if isinstance(result, set): return MySet(result) else: return result setattr(MySet, name, wrapper) return wrapper return attr

Al subclasificar tipos incorporados, noté una diferencia bastante importante entre Python 2 y Python 3 en el tipo de retorno de los métodos de los tipos incorporados. El siguiente código ilustra esto para conjuntos:

class MySet(set): pass s1 = MySet([1, 2, 3, 4, 5]) s2 = MySet([1, 2, 3, 6, 7]) print(type(s1.union(s2))) print(type(s1.intersection(s2))) print(type(s1.difference(s2)))

Con Python 2, todos los valores de retorno son de tipo MySet . Con Python 3, se set tipos de retorno. No pude encontrar ninguna documentación sobre el resultado que se supone que debe ser, ni ninguna documentación sobre el cambio en Python 3.

De todos modos, lo que realmente me importa es esto: ¿hay una forma sencilla en Python 3 para obtener el comportamiento visto en Python 2, sin redefinir cada método de los tipos integrados?


Esto no es un cambio general para los tipos incorporados al pasar de Python 2.x a 3.x; list y int , por ejemplo, tienen el mismo comportamiento en 2.xy 3.x. Solo se cambió el tipo de conjunto para alinearlo con los otros tipos, como se explica en este problema de seguimiento de errores .

Me temo que no hay una manera realmente buena de hacer que se comporte como antes. Aquí hay un código que pude encontrar:

class MySet(set): def copy(self): return MySet(self) def _make_binary_op(in_place_method): def bin_op(self, other): new = self.copy() in_place_method(new, other) return new return bin_op __rand__ = __and__ = _make_binary_op(set.__iand__) intersection = _make_binary_op(set.intersection_update) __ror__ = __or__ = _make_binary_op(set.__ior__) union = _make_binary_op(set.update) __sub__ = _make_binary_op(set.__isub__) difference = _make_binary_op(set.difference_update) __rxor__ = xor__ = _make_binary_op(set.__ixor__) symmetric_difference = _make_binary_op(set.symmetric_difference_update) del _make_binary_op def __rsub__(self, other): new = MySet(other) new -= self return new

Esto simplemente sobrescribirá todos los métodos con versiones que devuelvan su propio tipo. (¡Hay muchos métodos!)

Tal vez para su aplicación, puede salirse con sobrescribir copy() y atenerse a los métodos en el lugar.


Tal vez una metaclase para hacer todo ese envoltorio para usted lo haría más fácil:

class Perpetuate(type): def __new__(metacls, cls_name, cls_bases, cls_dict): if len(cls_bases) > 1: raise TypeError("multiple bases not allowed") result_class = type.__new__(metacls, cls_name, cls_bases, cls_dict) base_class = cls_bases[0] known_attr = set() for attr in cls_dict.keys(): known_attr.add(attr) for attr in base_class.__dict__.keys(): if attr in (''__new__''): continue code = getattr(base_class, attr) if callable(code) and attr not in known_attr: setattr(result_class, attr, metacls._wrap(base_class, code)) elif attr not in known_attr: setattr(result_class, attr, code) return result_class @staticmethod def _wrap(base, code): def wrapper(*args, **kwargs): if args: cls = args[0] result = code(*args, **kwargs) if type(result) == base: return cls.__class__(result) elif isinstance(result, (tuple, list, set)): new_result = [] for partial in result: if type(partial) == base: new_result.append(cls.__class__(partial)) else: new_result.append(partial) result = result.__class__(new_result) elif isinstance(result, dict): for key in result: value = result[key] if type(value) == base: result[key] = cls.__class__(value) return result wrapper.__name__ = code.__name__ wrapper.__doc__ = code.__doc__ return wrapper class MySet(set, metaclass=Perpetuate): pass s1 = MySet([1, 2, 3, 4, 5]) s2 = MySet([1, 2, 3, 6, 7]) print(s1.union(s2)) print(type(s1.union(s2))) print(s1.intersection(s2)) print(type(s1.intersection(s2))) print(s1.difference(s2)) print(type(s1.difference(s2)))