diferencia entre variables dentro y fuera de __init__() (atributos de clase e instancia)

9 minutos de lectura

Avatar de usuario de Teifion
Teifión

¿Hay alguna diferencia entre estas clases además del nombre?

class WithClass ():
    def __init__(self):
        self.value = "Bob"
    def my_func(self):
        print(self.value)

class WithoutClass ():
    value = "Bob"

    def my_func(self):
        print(self.value)

¿Hay alguna diferencia si uso o no uso el __init__ método para declarar la variable value?

Mi principal preocupación es que lo usaré de una manera, cuando eso me cause más problemas en el futuro.

  • Una respuesta detallada con ejemplos en la pregunta duplicada: stackoverflow.com/a/9056994/911945

    – Antón Tarasenko

    27 oct 2018 a las 15:23

Avatar de usuario de S. Lott
S. Lott

Conjunto de variables fuera __init__ pertenecen a la clase. Son compartidos por todas las instancias.

Variables creadas dentro __init__ (y todas las demás funciones del método) y precedida de self. pertenecen a la instancia del objeto.

  • Eso no es lo que Python hace por mí. Las listas/dictados/etc se comparten entre todas las instancias si no las crea en __init__().

    – demasiado php

    8 de octubre de 2009 a las 11:43

  • @demasiado php: todas las variables en el método de clase (independientemente de la mutabilidad, las listas y los dictados son mutables) se comparten. Con objetos inmutables, compartir no es interesante. Con objetos mutables (listas y dictados), el intercambio es significativo.

    – S. Lott

    8 de octubre de 2009 a las 11:58

  • Sospeché que ese podría ser el caso, pero pensé que si declaraba mis suposiciones, podría distraer la atención de la pregunta en sí, aplausos por aclararla 🙂

    – Teifión

    8 de octubre de 2009 a las 18:54

  • Pensé que esto era lo que estaba sucediendo, pero no estaba muy claro para mí. Gracias por aclararme.

    – dimo414

    22 de febrero de 2011 a las 10:49

  • Esto es un poco engañoso. A varnameconjunto de variables fuera del en eso pertenece a la clase, y se puede leer a través de self.varname produciendo el mismo valor para todas las instancias. Sin embargo, cuando se le asigna un valor a través de una referencia de instancia (como en self.varname = X) a nuevo Se creará self.varname solo para esa instancia, oscureciendo la variable de clase. La clase var permanece accesible a través de una referencia de clase (por ejemplo: WithClass.varname). Y las variables de clase también se pueden configurar desde cualquier método al comenzar con el nombre de la clase (WithClass.myvar = X) en lugar de una referencia de instancia (self.myvar = X).

    – Lobotomik

    8 de marzo de 2017 a las 11:26

sin uno mismo

Crea algunos objetos:

class foo(object):
    x = 'original class'

c1, c2 = foo(), foo()

Puedo cambiar la instancia c1 y no afectará a la instancia c2:

c1.x = 'changed instance'
c2.x
>>> 'original class'

Pero si cambio la clase foo, todas las instancias de esa clase también cambiarán:

foo.x = 'changed class'
c2.x
>>> 'changed class'

Tenga en cuenta cómo funciona el alcance de Python aquí:

c1.x
>>> 'changed instance'

Con uno mismo

Cambiar la clase no afecta a las instancias:

class foo(object):
    def __init__(self):
        self.x = 'original self'

c1 = foo()
foo.x = 'changed class'
c1.x
>>> 'original self'

  • tenga en cuenta que este ejemplo funciona solo para clases de tipo nuevas, con las antiguas, tendrán el mismo resultado

    – Abdelouahab

    4 de enero de 2015 a las 0:57

  • @Abdelouahab, ¿es así? Lo probé con clases de estilo antiguo y obtuve el mismo resultado que los objetos de estilo nuevo. Pitón 2.7.8

    – farmear

    6 de agosto de 2015 a las 1:01

avatar de usuario de alejandro
alejandro

Me gustaría agregar algo a las respuestas que leí en este hilo y este hilo (que hace referencia a este).

Descargo de responsabilidad: estos comentarios provienen de los experimentos que realicé

Variables fuera __init__:

Estos son, de hecho, variables de clase estáticas y son, por tanto, accesibles a todas las instancias de la clase.

Variables dentro __init__:

El valor de estos variables de instancia sólo son accesibles a la instancia en cuestión (a través del self referencia)

Mi contribución:

Una cosa que los programadores deben tener en cuenta al usar variables de clase estáticas es que pueden ser sombreados por variables de instancia (si está accediendo a la variables de clase estáticas a través de self referencia).

Explicación:

Anteriormente, pensaba que ambas formas de declarar las variables eran exactamente iguales (tonto de mí), y eso se debía en parte a que podía acceder a ambos tipos de variables a través del self referencia. Fue ahora, cuando me encontré con problemas, que investigué el tema y lo aclaré.

El problema de acceder variables de clase estáticas a través de
self referencia es que solo hace referencia a la variable de clase estática si no hay Instancia variable con el mismo nombre, y para colmo, intentando redefinir un variable de clase estática a través de self referencia no funciona porque un Instancia variable se crea que luego sombrea el previamente accesible variable de clase estática.

Para evitar este problema, siempre debe hacer referencia a variables de clase estáticas a través del nombre de la clase.

Ejemplo:

#!/usr/bin/env python

class Foo:
    static_var="every instance has access"

    def __init__(self,name):
        self.instance_var="I am %s" % name

    def printAll(self):
        print 'self.instance_var = %s' % self.instance_var
        print 'self.static_var = %s' % self.static_var
        print 'Foo.static_var = %s' % Foo.static_var

f1 = Foo('f1')

f1.printAll()

f1.static_var="Shadowing static_var"

f1.printAll()

f2 = Foo('f2')

f2.printAll()

Foo.static_var="modified class"

f1.printAll()
f2.printAll()

Producción:

self.instance_var = I am f1
self.static_var = every instance has access
Foo.static_var = every instance has access
self.instance_var = I am f1
self.static_var = Shadowing static_var
Foo.static_var = every instance has access
self.instance_var = I am f2
self.static_var = every instance has access
Foo.static_var = every instance has access
self.instance_var = I am f1
self.static_var = Shadowing static_var
Foo.static_var = modified class
self.instance_var = I am f2
self.static_var = modified class
Foo.static_var = modified class

Espero que esto sea útil para alguien.

  • Esta es la respuesta más importante aquí.

    – gothz

    27 de diciembre de 2021 a las 10:13

Avatar de usuario de Neil
neil

además de la respuesta de S. Lott, las variables de clase se pasan a la metaclase nuevo y se puede acceder a través del diccionario cuando se define una metaclase. Por lo tanto, se puede acceder a las variables de clase incluso antes de que se creen e instancian las clases.

por ejemplo:

class meta(type):
    def __new__(cls,name,bases,dicto):
          # two chars missing in original of next line ...
          if dicto['class_var'] == 'A':
             print 'There'
class proxyclass(object):
      class_var="A"
      __metaclass__ = meta
      ...
      ...

class User(object):
    email="none"
    firstname="none"
    lastname="none"

    def __init__(self, email=None, firstname=None, lastname=None):
        self.email = email
        self.firstname = firstname
        self.lastname = lastname

    @classmethod
    def print_var(cls, obj):
        print ("obj.email obj.firstname obj.lastname")
        print(obj.email, obj.firstname, obj.lastname)
        print("cls.email cls.firstname cls.lastname")
        print(cls.email, cls.firstname, cls.lastname)

u1 = User(email="abc@xyz", firstname="first", lastname="last")
User.print_var(u1)

En el código anterior, la clase Usuario tiene 3 variables globales, cada una con valor ‘ninguno’. u1 es el objeto creado al instanciar esta clase. El método print_var imprime el valor de las variables de clase de la clase Usuario y las variables de objeto del objeto u1. En el resultado que se muestra a continuación, cada una de las variables de clase User.email, User.firstname y User.lastname tiene valor 'none'mientras que las variables de objeto u1.email, u1.firstname y u1.lastname tener valores 'abc@xyz', 'first' y 'last'.

obj.email obj.firstname obj.lastname
('abc@xyz', 'first', 'last')
cls.email cls.firstname cls.lastname
('none', 'none', 'none')

En Python, una clase viene con funciones miembro (métodos), variables de clase, atributos/variables de instancia (y probablemente métodos de clase también):

class Employee:

    # Class Variable
    company = "mycompany.com"

    def __init__(self, first_name, last_name, position):
        # Instance Variables
        self._first_name = first_name
        self._last_name = last_name
        self._position = position

    # Member function
    def get_full_name(self):
        return f"{self._first_name} {self._last_name}"

Creando una instancia del objeto

my_employee = Employee("John", "Wood", "Software Engineer")

esencialmente desencadenamos __init__ que va a inicializar el variables de instancia de la recién creada Employee. Esto significa que _first_name, _last_name y _position son parámetros explícitos de lo específico my_employee instancia.

Igualmente, funciones miembro devolver información o cambiar el estado de una instancia específica.


Ahora cualquier variable definida fuera del constructor __init__ se consideran ser variables de clase. Esas variables se comparten entre todas las instancias de la clase.

john = Employee("John", "Wood", "Software Engineer")
bob = Employee("Bob", "Smith", "DevOps Engineer0")

print(john.get_full_name())
print(bob.get_full_name())
print(john.company)
print(bob.company)

>>> John Wood
>>> Bob Smith
>>> mycompany.com
>>> mycompany.com

También puedes usar métodos de clase para cambiar la variable de clase para todas las instancias de la clase. Por ejemplo:

@classmethod
def change_my_companys_name(cls, name):
    cls.company = name

y ahora change_my_companys_name()

bob.change_my_companys_name("mynewcompany.com")

tendrá efecto en todas las instancias de la clase Employee:

print(bob.company)
print(john.company)

>>> mynewcompany.com
>>> mynewcompany.com

Avatar de usuario de John Friday
juan viernes

Código de ejemplo:

class inside:
    def __init__(self):
        self.l = []

    def insert(self, element):
        self.l.append(element)


class outside:
    l = []             # static variable - the same for all instances

    def insert(self, element):
        self.l.append(element)


def main():
    x = inside()
    x.insert(8)
    print(x.l)      # [8]
    y = inside()
    print(y.l)      # []
    # ----------------------------
    x = outside()
    x.insert(8)
    print(x.l)      # [8]
    y = outside()
    print(y.l)      # [8]           # here is the difference


if __name__ == '__main__':
    main()

  • Pero si uso L para almacenar un int, puedo cambiar la L incluso por la x exterior, sin cambiar la L de la y exterior…

    –Oskar Hofmann

    5 de julio de 2021 a las 11:29

¿Ha sido útil esta solución?