Comencé a aprender clases de python hace algún tiempo, y hay algo que no entiendo cuando se trata del uso de self.variables dentro de una clase. Busqué en Google, pero no pude encontrar la respuesta. No soy un programador, solo un aficionado a las pitones. Aquí hay un ejemplo de una clase simple, con dos formas de definirla:

1) primera forma:

class Testclass:
    def __init__(self, a,b,c):
        self.a = a
        self.b = b
        self.c = c
    def firstMethod(self):
        self.d = self.a + 1
        self.e = self.b + 2
    def secondMethod(self):
        self.f = self.c + 3
    def addMethod(self):
        return self.d + self.e + self.f

myclass = Testclass(10,20,30)
myclass.firstMethod()
myclass.secondMethod()
addition = myclass.addMethod()

2) segunda forma:

class Testclass:
    def __init__(self, a,b,c):
        self.a = a
        self.b = b
        self.c = c
    def firstMethod(self):
        d = self.a + 1
        e = self.b + 2
        return d,e
    def secondMethod(self):
        f = self.c + 3
        return f
    def addMethod(self, d, e, f):
        return d+e+f

myclass = Testclass(10,20,30)
d, e = myclass.firstMethod()
f= myclass.secondMethod()
addition = myclass.addMethod(d,e,f)

Lo que me confunde es ¿cuál de estos dos es válido? ¿Es mejor definir siempre las variables dentro de los métodos (las variables que esperamos usar más adelante) como self.variables (lo que las haría globales dentro de la clase) y luego simplemente llamarlas dentro de algún otro método de esa clase (eso sería la primera forma en el código superior)? ¿O es mejor no definir variables dentro de los métodos como self.variables, sino simplemente como variables regulares, y luego regresar al final del método. ¿Y luego "reimportarlos" de nuevo a algún otro método como sus argumentos (eso sería la segunda forma en el código superior)?

EDITAR: solo para que quede claro, no quiero definir las variables self.d, self.e, self.f o d, e, f bajo el método init . Quiero definirlos en algunos otros métodos como se muestra en el código superior. Perdón por no mencionar eso.

2
marco 29 ago. 2014 a las 05:46

3 respuestas

La mejor respuesta

Ambos son enfoques válidos. Cuál es el correcto depende completamente de la situación.

E.g.

  • Donde 'realmente' está obteniendo los valores de a, b, c de
  • ¿Desea / necesita usarlos varias veces?
  • ¿Desea / necesita usarlos dentro de otros métodos de la clase?
  • ¿Qué representa la clase?
  • ¿Son los atributos b y c realmente "fijos" de la clase, o dependen de factores externos?

En el ejemplo que da en el comentario a continuación:

Digamos que a, b, c dependen de algunas variables externas (por ejemplo a = d + 10, b = e + 20, c = f + 30, donde d, e, f se suministran al crear instancias de una clase: {{X0 }}. Sí, digamos que quiero usar variables a, b, c (o self.a, self.b, self.c) dentro de otros métodos de la clase también.

Entonces, en ese caso, el enfoque 'correcto' depende principalmente de si espera que a, b, c cambie durante la vida de la instancia de clase. Por ejemplo, si tiene una clase en la que los atributos (a, b, c) nunca cambiarán rara vez, pero utiliza las atribuciones derivadas (d, e, f) en gran medida, entonces tiene sentido calcularlas una vez y almacenarlas . Aquí hay un ejemplo:

class Tiger(object):
    def __init__(self, num_stripes):
        self.num_stripes = num_stripes
        self.num_black_stripes = self.get_black_stripes()
        self.num_orange_stripes = self.get_orange_stripes()
    def get_black_stripes(self):
        return self.num_stripes / 2
    def get_orange_stripes(self):
        return self.num_stripes / 2

big_tiger = Tiger(num_stripes=200)
little_tiger = Tiger(num_stripes=30)

# Now we can do logic without having to keep re-calculating values
if big_tiger.num_black_stripes > little_tiger.num_orange_stripes:
    print "Big tiger has more black stripes than little tiger has orange"

Esto funciona bien porque cada tigre individual tiene un número fijo de rayas. Si cambiamos el ejemplo para usar una clase para la cual las instancias cambiarán con frecuencia, entonces nuestro enfoque también cambia:

class BankAccount(object):
    def __init__(self, customer_name, balance):
        self.customer_name = customer_name
        self.balance = balance
    def get_interest(self):
        return self.balance / 100

my_savings = BankAccount("Tom", 500)
print "I would get %d interest now" % my_savings.get_interest()
# Deposit some money
my_savings.balance += 100
print "I added more money, my interest changed to %d" % my_savings.get_interest()

Por lo tanto, en este ejemplo (algo artificial), el saldo de una cuenta bancaria cambia con frecuencia, por lo tanto, no tiene ningún valor almacenar el interés en una variable de interés propio. Cada vez que cambia el saldo, el monto del interés también cambiará. Por lo tanto, tiene sentido calcularlo cada vez que necesitemos usarlo.

Hay una serie de enfoques más complejos que puede tomar para obtener algún beneficio de ambos. Por ejemplo, puede hacer que su programa 'sepa' que el interés está vinculado al saldo y luego recordará temporalmente el valor del interés hasta que el saldo cambie (esta es una forma de almacenamiento en caché; usamos más memoria pero ahorramos algo de CPU / cálculo).

No relacionado con la pregunta original

Una nota sobre cómo declaras tus clases. Si está utilizando Python 2, es una buena práctica hacer que sus propias clases hereden de la clase de objeto integrada de Python:

class Testclass(object):
    def __init__(self, printHello):

Ref. https://wiki.python.org/moin/NewClassVsClassicClass Python 3 usa allí clases de estilo nuevo de forma predeterminada, por lo que no necesita heredar explícitamente del objeto si usa py3.

3
Tom Dalton 7 abr. 2017 a las 07:25

Realmente no puede sacar ninguna conclusión sobre este tipo de pregunta en ausencia de un ejemplo concreto y significativo, porque dependerá de los hechos y circunstancias de lo que está tratando de hacer.

Dicho esto, en su primer ejemplo, firstMethod() y secondMethod() son superfluos. No tienen otro propósito que calcular los valores que addMethod() usa. Peor aún, para que addMethod() funcione, el usuario primero tiene que hacer dos llamadas inexplicables y aparentemente no relacionadas a firstMethod() y secondMethod(), que sin duda es un mal diseño. Si esos dos métodos realmente hicieron algo significativo, podría tener sentido (pero probablemente no), pero en ausencia de un ejemplo real es simplemente malo.

Puede reemplazar el primer ejemplo por:

class Testclass:
    def __init__(self, a,b,c):
        self.a = a
        self.b = b
        self.c = c

    def addMethod(self):
        return self.a + self.b + self.c + 6

myclass = Testclass(10,20,30)
addition = myclass.addMethod()

El segundo ejemplo es similar, excepto que firstMethod() y secondMethod() realmente hacen algo, ya que devuelven valores. Si hubiera algún motivo por el que desearía estos valores por separado por algún motivo que no sea pasarlos a addMethod(), entonces nuevamente, podría tener sentido. Si no existiera, entonces nuevamente podría definir addMethod() como lo acabo de hacer, y prescindir de esas dos funciones adicionales por completo, y no habría ninguna diferencia entre los dos ejemplos.

Pero todo esto es muy insatisfactorio en ausencia de un ejemplo concreto. En este momento, todo lo que realmente podemos decir es que es una clase un poco tonta.

En general, los objetos en el sentido OOP son conglomerados de datos (variables de instancia) y comportamiento (métodos). Si un método no accede a las variables de instancia, o no necesita hacerlo, entonces generalmente debería ser una función independiente y no estar en una clase en absoluto. De vez en cuando tendrá sentido tener una clase o método estático que no acceda a las variables de instancia, pero en general debe equivocarse para preferir las funciones independientes.

1
Crowman 29 ago. 2014 a las 03:32

EDITADA:

Si desea conservar los valores dentro del objeto después de realizar addMethod, por ejemplo, si desea llamar a addMethod nuevamente. luego use la primera forma. Si solo desea usar algunos valores internos de la clase para realizar el addMethod, use la segunda forma.

1
levi 29 ago. 2014 a las 02:20