2009-08-09 20 views
12

Estoy interesado en escuchar alguna discusión sobre los atributos de clase en Python. Por ejemplo, ¿cuál es un buen caso de uso para los atributos de clase? En general, no puedo encontrar un caso en el que un atributo de clase sea preferible al uso de un atributo de nivel de módulo. Si esto es cierto, entonces ¿por qué tenerlos?Python Clase vs. Módulo Atributos

El problema que tengo con ellos es que es casi demasiado fácil destruir un valor de atributo de clase por error, y entonces su valor "global" se ha convertido en un atributo de instancia local.

Siéntase libre de comentar sobre cómo manejaría las siguientes situaciones:

  1. valores constantes utilizados por una clase y/o sub-clases. Esto puede incluir claves de diccionario de "número mágico" o índices de lista que nunca cambiarán, pero que posiblemente necesiten una inicialización única.
  2. Atributo de clase predeterminado, que en raras ocasiones se actualizó para una instancia especial de la clase.
  3. Estructura de datos global utilizada para representar un estado interno de una clase compartida entre todas las instancias.
  4. Clase que inicializa una serie de atributos predeterminados, sin influencia de los argumentos del constructor.

Algunos Artículos Relacionados:
Difference Between Class and Instance Attributes

+0

debería ser wiki de la comunidad – SilentGhost

Respuesta

7

# 4: que nunca se clase de uso de atributos para inicializar instancia predeterminada atributos (las que normalmente se pone en __init__). Por ejemplo:

class Obj(object): 
    def __init__(self): 
     self.users = 0 

y nunca:

class Obj(object): 
    users = 0 

¿Por qué? Debido a que es inconsistente: no hace lo que quiere cuando se asigna otra cosa que un objeto invariante:

class Obj(object): 
    users = [] 

provoca lista para ser compartida a través de todos los objetos, que en este caso no se querían los usuarios. Es confuso dividir estos en atributos de clase y asignaciones en __init__ dependiendo de su tipo, por lo que siempre los pongo todos en __init__, que de todos modos me parecen más claros.


En cuanto al resto, generalmente pongo valores específicos de clase dentro de la clase.Esto no es tanto porque los globales son "malvados", no son tan importantes como en algunos idiomas, porque todavía tienen alcance en el módulo, a menos que el módulo en sí sea demasiado grande, pero si el código externo quiere acceder a ellos, es útil tener todos los valores relevantes en un solo lugar. Por ejemplo, en module.py:

class Obj(object): 
    class Exception(Exception): pass 
    ... 

y luego:

from module import Obj 

try: 
    o = Obj() 
    o.go() 
except o.Exception: 
    print "error" 

Aparte de permitir que las subclases para cambiar el valor (que no siempre se quiere de todos modos), significa que no tengo para importar laboriosamente nombres de excepción y un montón de otras cosas necesarias para usar Obj. "desde el módulo de importación Obj, ObjException, ..." se vuelve tedioso rápidamente.

2

Los atributos de clase a menudo se utilizan para permitir que los valores predeterminados imperiosas de subclases. Por ejemplo, BaseHTTPRequestHandler tiene las constantes de clase sys_version y server_version, esta última por defecto a "BaseHTTP/" + __version__. SimpleHTTPRequestHandler anula server_version a "SimpleHTTP/" + __version__.

+0

Sin embargo, el método de acceso del atributo de clase sería un factor si esto funcionaba o no. Otra razón por la cual los atributos de clase son peligrosos. – cmcginty

+0

¿Qué "método de acceso"? cls.version, self.version, getattr, todos darán la respuesta correcta. * Definir en clase para permitir invalidaciones * funciona igual de bien para los datos que para el código, entonces, Casey, ¿por qué crees que es peligroso para los datos pero está bien para el código? –

+0

Al utilizar Class.value en lugar de self.value en un método para acceder al atributo evitará que ocurra una anulación.Algunas veces este es el caso deseado, otras veces no. – cmcginty

1

La encapsulación es un buen principio: cuando un atributo se encuentra dentro de la clase, pertenece en lugar de estar en el ámbito global, esto proporciona información adicional a las personas que leen el código.

En sus situaciones 1-4, evitaría tanto globales como pueda, y prefiero usar atributos de clase, que permiten beneficiarse de la encapsulación.

3

lo que es un buen caso de uso de los atributos de clase

Caso 0. Los métodos de clase son sólo atributos de clase. Esto no es solo una similitud técnica: puede acceder y modificar los métodos de clase en tiempo de ejecución asignándoles callables.

Caso 1. Un módulo puede definir fácilmente varias clases. Es razonable encapsular todo sobre class A en A... y todo sobre class B en B.... Por ejemplo,

# module xxx 
class X: 
    MAX_THREADS = 100 
    ... 

# main program 
from xxx import X 

if nthreads < X.MAX_THREADS: ... 

Caso 2. Esta clase tiene un montón de atributos predeterminados que se pueden modificar en una instancia. Aquí, la capacidad de dejar el atributo como un 'predeterminado global' es una característica, no un error.

class NiceDiff: 
    """Formats time difference given in seconds into a form '15 minutes ago'.""" 

    magic = .249 
    pattern = 'in {0}', 'right now', '{0} ago' 

    divisions = 1 

    # there are more default attributes 

Uno crea instancia de NiceDiff utilizar el formato existente o ligeramente modificada, sino un localizador a un idioma diferente subclases de la clase para implementar algunas de las funciones de una manera fundamentalmente diferente y redefinir constantes:

class Разница(NiceDiff): # NiceDiff localized to Russian 
    '''Из разницы во времени, типа -300, делает конкретно '5 минут назад'.''' 

    pattern = 'через {0}', 'прям щас', '{0} назад' 

Sus casos:

  • constantes - ye s, los puse a clase. Es extraño decir self.CONSTANT = ..., por lo que no veo un gran riesgo de castigarlos.
  • Atributo predeterminado: mixto, como se indica anteriormente, puede ir a la clase, pero también puede ir a __init__ dependiendo de la semántica.
  • Estructura de datos global --- va a clase si se usa solo por la clase, pero también puede ir al módulo, en cualquier caso debe ser muy bien documentado.
Cuestiones relacionadas