2010-04-16 6 views
12

Estoy usando Python 2.x y me pregunto si hay una manera de saber si una variable es una clase de estilo nuevo. Sé que si se trata de una clase de estilo antiguo, puedo hacer lo siguiente para averiguarlo.¿Identificando que una variable es una clase de estilo nuevo en Python?

import types 

class oldclass: 
    pass 

def test(): 
    o = oldclass() 
    if type(o) is types.InstanceType: 
    print 'Is old-style' 
    else: 
    print 'Is NOT old-style' 

Pero no he podido encontrar nada que funcione para las clases de nuevo estilo. Encontré this question, pero las soluciones propuestas no parecen funcionar como se esperaba, porque los valores simples se identifican como clases.

import inspect 

def newclass(object): 
    pass 

def test(): 
    n = newclass() 
    if inspect.isclass(n): 
    print 'Is class' 
    else: 
    print 'Is NOT class' 
    if inspect.isclass(type(n)): 
    print 'Is class' 
    else: 
    print 'Is NOT class' 
    if inspect.isclass(type(1)): 
    print 'Is class' 
    else: 
    print 'Is NOT class' 
    if isinstance(n, object): 
    print 'Is class' 
    else: 
    print 'Is NOT class' 
    if isinstance(1, object): 
    print 'Is class' 
    else: 
    print 'Is NOT class' 

¿Hay alguna forma de hacer algo como esto? ¿O todo en Python es solo una clase y no hay forma de evitarlo?

+0

Por curiosidad, ¿por qué? –

+1

Estaba trabajando en una clase que serializaba a sus hijos recursivamente y quería poder decir si el niño era una clase y manejarlo también serializando sus hijos recursivamente. –

+0

su comentario anterior parece que debería ser la pregunta real, y la pregunta original parece un paso (posiblemente erróneo) que dio hacia la respuesta a su problema. Mira esto: http://catb.org/~esr/faqs/smart-questions.html#goal – tzot

Respuesta

7

Creo que lo que estás preguntando es: "¿Puedo probar si una clase se definió en el código de Python como una clase de nuevo estilo?". Los tipos técnicamente simples como intson clases de nuevo estilo, pero aún es posible distinguir clases escritas en Python de los tipos incorporados.

Aquí hay algo que funciona, aunque es un poco de un truco:

def is_new_style(cls): 
    return hasattr(cls, '__class__') \ 
      and \ 
      ('__dict__' in dir(cls) or hasattr(cls, '__slots__')) 


class new_style(object): 
    pass 

class old_style(): 
    pass 

print is_new_style(int) 
print is_new_style(new_style) 
print is_new_style(old_style) 

salida desde Python 2.6:

False 
True 
False 

Aquí está una manera diferente de hacerlo:

def is_new_style(cls): 
    return str(cls).startswith('<class ') 
+0

Eso hizo el truco. Gracias. –

+0

Además, en base a la respuesta de ~ unutbu, el se puede utilizar para identificar las clases de estilo antiguo: def is_old_style (CLS): retorno no hasattr (CLS, '__init__') –

+0

Lo siento por la falta de información, pero lo anterior is_old_style (cls) no funciona si la clase old-style tiene un método \ __ init \ __ init definido explícitamente, pero type (cls) es types.InstanceType funciona. –

1

No es que "todo es una clase": con lo que se topan es que "todo es un objeto " (es decir, cada cosa (de nuevo estilo) desciende de "objeto").

Pero las clases de nuevo estilo son un "tipo" ellos mismos (en realidad, se introdujeron para reunir clases y tipos). Por lo tanto, puede intentar buscar

import types 

type(o) == types.TypeType 

¿Eso soluciona su problema?

+0

Devuelve True for int también –

+0

No, esa expresión para las clases de estilo antiguo y nuevo se evalúa como falsa. Sin embargo, esto se evalúa como verdadera para las clases de nuevo estilo, pero no las clases de estilo antiguo: tipo (n .__ class__) == types.TypeType Pero, entonces también se evalúa como verdadera para los tipos básicos como 'int ', entonces no es exactamente lo que estaba buscando. –

+0

Correcto, un int es una instancia de una clase de estilo nuevo. Ese es el objetivo de la unificación. –

1

Creo que esto es suficiente:

def is_new_style_class(klass): 
    return issubclass(klass, object) 

def is_new_style_class_instance(instance): 
    return issubclass(instance.__class__, object) 

Normalmente, solo necesita la función is_new_style_class para sus fines. No todo una clase lanzará una TypeError, por lo que es posible que desee actualizar a:

def is_new_style_class(klass): 
    try: 
     return issubclass(klass, object) 
    except TypeError: 
     return False 

Ejemplos:

>>> class New(object): pass 
... 
>>> is_new_style_class(New) 
True 
>>> class Old: pass 
... 
>>> is_new_style_class(Old) 
False 
>>> is_new_style_class(1) 
False 
>>> is_new_style_class(int) 
True 

int, siendo un tipo, es por definición una clase de nuevo estilo (ver Unifying types and classes in Python 2.2) o, si prefiere, las clases de nuevo estilo son por definición tipos.

+0

El problema con eso es que todo (incluso las clases de estilo antiguo y los tipos básicos como int) devolverá verdadero. –

+0

Los problemas con su comentario son: las clases antiguas devuelven False, y 'int' (que es un tipo, por definición) * es * una clase de estilo nuevo. – tzot

+0

El problema es que int no tiene un atributo \ __ dict \ __ y por lo tanto infringe una de las "reglas" de las clases de estilo nuevo. –

-1

La comprobación de las clases antiguas es muy fácil. Simplemente marque type(cls) is types.ClassType. También es fácil verificar las clases de nuevo estilo, isinstance(cls, type). Tenga en cuenta que los tipos incorporados también son clases de estilo nuevo.

Parece que no hay una forma trivial de distinguir las incorporaciones de las clases escritas en Python.Las clases de nuevo estilo con __slots__ tampoco tienen __dict__, al igual que int o str. Comprobar si str (cls) coincide con el patrón esperado falla si la metaclase de las clases prevalece sobre el método __str__. Algunas otras maneras que tampoco funcionan:

  • cls.__module__ == '__builtin__' (puede reasignar __module__ de clases)
  • not any(value is cls for value in vars(__builtins__).values()) (se pueden agregar cosas al módulo __builtin__).

El hecho de que la unificación de tipos incorporados y definidos por el usuario es tan bueno que distinguirlos no es un problema trivial debería implicar para usted el punto subyacente. Realmente no deberías tener que distinguir entre ellos. No importa cuál sea el objeto si implementa el protocolo esperado.

+0

'types.ClassType' es [específico de Python 2] (https://stackoverflow.com/questions/529240/what-happened-to-types-classtype-in-python-3), haciendo que esta respuesta sea obsoleta. Además, hay _es_ un [medio confiable] (https://stackoverflow.com/a/12237216/2809027) de distinguir entre clases integradas y definidas por el usuario bajo CPython. –

Cuestiones relacionadas