Lo que sigue es un hack horrible que utiliza las características de Python documentadas e indocumentadas. Nunca debe alguna vez hacer algo como esto.
Ha sido probado en Python 2.6.1 y 2.7.2; no parece funcionar con Python 3.2 como está escrito, pero luego, puedes do this right en Python 3.x de todos modos.
import sys
class NoDupNames(object):
def __init__(self):
self.namespaces = []
def __call__(self, frame, event, arg):
if event == "call":
if frame.f_code.co_flags == 66:
self.namespaces.append({})
elif event in ("line", "return") and self.namespaces:
for key in frame.f_locals.iterkeys():
if key in self.namespaces[-1]:
raise NameError("attribute '%s' already declared" % key)
self.namespaces[-1].update(frame.f_locals)
frame.f_locals.clear()
if event == "return":
frame.f_locals.update(self.namespaces.pop())
return self
def __enter__(self):
self.oldtrace = sys.gettrace()
sys.settrace(self)
def __exit__(self, type, value, traceback):
sys.settrace(self.oldtrace)
Uso:
with NoDupNames():
class Foo(object):
num = None
num = 42
Resultado:
NameError: attribute 'num' already declared
Cómo funciona: Nos conectamos con el gancho de rastreo del sistema. Cada vez que Python está a punto de ejecutar una línea, se nos llama. Esto nos permite ver qué nombres fueron definidos por la última instrucción ejecutada. Para asegurarnos de que podamos capturar duplicados, en realidad mantenemos nuestro propio diccionario de variables locales y borramos Python después de cada línea. Al final de la definición de la clase, copiamos nuestros locales en Python. Algunas de las otras tonterías están ahí para manejar las definiciones de clases anidadas y para manejar múltiples asignaciones en una sola declaración.
Como un inconveniente, nuestro "¡despeja TODOS los lugareños!"Enfoque significa que no se puede hacer esto:
with NoDupNames():
class Foo(object):
a = 6
b = 7
c = a * b
Porque hasta donde sabe Python, no hay nombres a
y b
cuando se ejecuta c = a * b
; quitamos los tan pronto como vimos 'em Además, si. se asigna la misma variable en dos ocasiones en una sola línea (por ejemplo, a = 0; a = 1
) no va a coger eso. sin embargo, funciona para las definiciones de clases más típicas.
Además, no se debe poner nada, además de las definiciones de clases dentro de una NoDupNames
contexto. No sé qué va a pasar, tal vez nada malo. Pero no lo he intentado, así que en teoría el el universo podría ser aspirado en su propio agujero.
Este es posiblemente el código más malvado que he escrito, ¡pero seguro que fue divertido!
Copiar y pegar es generalmente un signo de mala práctica de codificación; la mayoría de las veces significa que debe extraer la funcionalidad en otra función. Te ahorrará tiempo, esfuerzo si quieres cambiarlo más tarde, y problemas como estos. –
Lattyware: en general, estoy de acuerdo. Sin embargo, este es el código de prueba unitaria, en el que es una buena práctica y se espera que tenga un conjunto de métodos cortos, la mayoría de los cuales se parecen bastante, excepto por las diferentes condiciones de configuración y aserciones. Entonces, yo diría que cortar y pegar es lo normal para el curso y no representa una mala práctica. –
@Scotty: ¿Por qué no te haces el hábito de cambiar el nombre de un método como tu primera acción después de copiarlo? Por cierto, si el código de configuración y las aserciones difieren para cada método, entonces lo único que realmente está copiando es el nombre del método, que tal vez haya notado. –