【问题标题】:The use of self in multiple methods of a class在一个类的多个方法中使用self
【发布时间】:2017-08-12 00:43:34
【问题描述】:

所以基本上作为一个初学者程序员,我创建了这个程序,它使用 Tkinter 计算 pokemon go 中不同 pokemon/糖果组合的最大可能进化。我在使用所有代码的函数之前创建了这个程序。但是,我认为使用类会很好,经过一些研究,这就是结果。但是,我基本上是通过将 self 添加到不同方法中的一大堆变量来使其工作的。

我的问题是:在这里使用类是否相关。我对类的研究涉及用对象等填充类,但在这里我只创建一个对象以使用该类并运行一堆计算。

此外,我如何知道在调用变量时何时使用 self。例如,我创建静态类变​​量的印象是我不必将实例 self 放在它前面来使用它,但这不起作用,所以我只是在变量调用前面放了更多 self 。为什么把一堆自我放在一切前面会起作用?自我是不是矫枉过正,我可以用更聪明的方式来做吗?

from tkinter import *
from tkinter import messagebox


class PokeCalculator(Frame):
    pokedex = {
        'pidgey': 12,
        'caterpie': 12,
        'weedle': 12,
        'rattata': 25
    }
    choices = pokedex.keys()
    screen_title = 'Pokemon evolve calculator'

    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.master = master
        self.make_window()

    def make_window(self):
        self.master.title(self.screen_title)

        L1 = Label(self, text='Candies')
        L1.grid(column=1, row=0)

        L2 = Label(self, text='Pokemon amount in storage')
        L2.grid(column=2, row=0)

        self.var = StringVar()
        self.var.set('Pokemon')
        self.Pokemon = OptionMenu(self, self.var, *self.choices)
        self.Pokemon.grid(column=0, row=1)

        self.Candies = Entry(self)
        self.Candies.grid(column=1, row=1)

        self.Poke_amount = Entry(self)
        self.Poke_amount.grid(column=2, row=1)

        Calculate = Button(self, text='Calculate', command=self.get_and_check)
        Calculate.grid(column=1, row=2)

    def get_and_check(self):
        self.get_values()
        self.check_input()

    def get_values(self):
        self.poke = self.var.get()
        self.candies_total = self.Candies.get()
        self.p_amount = self.Poke_amount.get()

    def check_input(self):
        string1 = 'Please select a Pokemon from the dropdown menu'
        string2 = 'Please only enter numbers'
        if self.poke == 'Pokemon':
            messagebox.showinfo(self.screen_title, string1)
        elif not self.p_amount.isdigit() or not self.candies_total.isdigit():
            messagebox.showinfo(self.screen_title, string2)
        else:
            self.too_few_pokemon()

    def too_few_pokemon(self):
        candies_total = int(self.candies_total)
        p_amount = int(self.p_amount)
        evolve = int((candies_total - 1) / (self.pokedex[self.poke] - 1))
        candies_needed = (p_amount * (self.pokedex[self.poke] - 1)) + 1
        if p_amount <= evolve:
            n = 0
            while candies_needed <= candies_total:
                n = n + 1
                p_amount = p_amount + 1
                candies_needed = ((p_amount) * (self.pokedex[self.poke] - 1)) + 1
                candies_total = candies_total + 3
                evolve2 = int((candies_total - 1) / (self.pokedex[self.poke] - 1))
            string1 = '''            You have enough candies too evolve {0} {1},
            but you only have {2} {1} in storage and thus can only
            evolve {2} {1}.
            If you catch {3} more {1} you can now evolve {4} {1}.'''
            messagebox.showinfo(self.screen_title, string1.format(evolve, self.poke,
                                                                  self.p_amount,
                                                                  n, evolve2))
        else:
            self.too_much_pokemon()

    def too_much_pokemon(self):
        candies_total = int(self.candies_total)
        p_amount = int(self.p_amount)
        candies_needed = (p_amount * (self.pokedex[self.poke] - 1)) + 1
        m = 0
        while candies_total <= candies_needed:
            m = m + 1
            p_amount = p_amount - 1
            candies_needed = ((p_amount) * (self.pokedex[self.poke] - 1)) + 1
            candies_total = candies_total + 1
            evolve = int((candies_total - 1) / (self.pokedex[self.poke] - 1))
        string2 = 'Transfer {0} {1} so you can evolve a total of {2} {1}.'
        messagebox.showinfo(self.screen_title, string2.format(m, self.poke, evolve))


root = Tk()
app = PokeCalculator(root)
app.pack()
root.mainloop()

【问题讨论】:

标签: python python-3.x class tkinter self


【解决方案1】:

考虑以下示例:

class A:
    a_class_attribute = 'A class attribute'
    def __init__(self, a):
        self.a_instance_attribute = a
    def amethod(self):
        print('a method')

class B(A):
    b_class_attribute = 'B class attribute'
    def __init__(self, b):
        self.b_instance_attribute = b
    def bmethod(self):
        print('b method')

现在,让我们创建一个A 的实例:

In [2]: a = A('foo')

注意,我们可以通过查看实例的__dict__来查看该实例有哪些属性,

In [3]: a.__dict__
Out[3]: {'a_instance_attribute': 'foo'}

这是有道理的,因为我们有一个实例属性。上课呢?

In [7]: A.__dict__
Out[7]:
mappingproxy({'__dict__': <attribute '__dict__' of 'A' objects>,
              '__doc__': None,
              '__init__': <function self_oop_ex.A.__init__>,
              '__module__': 'self_oop_ex',
              '__weakref__': <attribute '__weakref__' of 'A' objects>,
              'a_class_attribute': 'A class attribute',
              'amethod': <function self_oop_ex.A.amethod>})

所以a_class_attribute 只是一个属于类的属性。请注意,方法也属于类,您可以将方法仅视为恰好是函数的类对象。不过,还有更多的魔法发生。但首先,您需要记住,当您访问实例上的属性时,例如my_instance.some_attribute,python 首先检查对象命名空间(即__dict__),如果找到该属性,则返回它。如果没有,它会在类的命名空间中查找属性!所以,这就是为什么我们可以从一个类和一个实例中访问一个类属性:

In [8]: A.a_class_attribute
Out[8]: 'A class attribute'

In [9]: a.a_class_attribute
Out[9]: 'A class attribute'

现在,这与self 有什么关系?好吧,Python 做的一件神奇的事情是,如果在您访问实例上的属性时找到了一个方法,如果您调用该方法,它隐式地将实例作为第一个参数传递,即我们通常致电self。所以,

In [10]: a.amethod()
a method

相当于:

In [11]: A.amethod(a)
a method

这就是方法的全部内容!它就像任何其他函数一样,只是如果从实例访问函数,Python 通过隐式传递实例来帮我们一个忙。所以请注意,我们可以做这样的事情:

In [17]: class Krazy:
    ...:     def use_pop(self):
    ...:         return self.pop()
    ...:     def pop(self):
    ...:         print("I am a Krazy pop")
    ...:

In [18]: k = Krazy()

In [19]: k.pop()
I am a Krazy pop

In [20]: mylist = [1,2,3,4,5]

In [21]: mylist.pop()
Out[21]: 5

In [22]: k.use_pop()
I am a Krazy pop

In [23]: Krazy.use_pop(mylist)
Out[23]: 4

注意,use_pop 方法使用self.pop,正如我们对任何函数所期望的那样,它调用self 的适当pop 方法,当我们使用Krazy.use_pop(mylist) 时, pop 属于列表对象

最后,考虑派生自A 的类BB 实例可以访问它们自己的实例属性,以及B 类和A 类的类属性。简而言之就是继承:

In [25]: b = B('bar')

In [26]: b.__dict__
Out[26]: {'b_instance_attribute': 'bar'}

In [27]: B.__dict__
Out[27]:
mappingproxy({'__doc__': None,
              '__init__': <function self_oop_ex.B.__init__>,
              '__module__': 'self_oop_ex',
              'b_class_attribute': 'B class attribute',

              'bmethod': <function self_oop_ex.B.bmethod>})

In [28]: b.amethod()
a method

In [29]: b.bmethod()
b method

In [30]: b.a_class_attribute
Out[30]: 'A class attribute'

In [31]: b.b_class_attribute
Out[31]: 'B class attribute'

当然,多重继承会变得更复杂,但现在,试着担心简单的继承情况。

【讨论】:

  • 谢谢。包含的继承说明不是我以前遇到过的,但是很有帮助。
  • 不要用继承把他吓跑 :D
【解决方案2】:

self 指的是该类的实例(对象)。 python中的类中有两种类型的属性。类属性和实例属性。

https://docs.python.org/2/tutorial/classes.html#class-and-instance-variables

实例属性特定于类的实例(对象)。
类属性是相对于类的。这些属性由类的所有实例共享。

在您的代码中,pokedexchoicesscreen_title 都是类属性的示例。 self.masterself.varself.Pokemon 等属性是实例属性的示例。

类的使用在这里是否相关?

我会说是的。类是数据和方法的逻辑分组。它封装了一些数据和一些可以对该数据执行的方法。我们不只是将随机的事物放在一个类中,而是尝试创建事物之间存在逻辑联系的类。

这可能是一个有用的来源: https://jeffknupp.com/blog/2014/06/18/improve-your-python-python-classes-and-object-oriented-programming/

【讨论】:

  • 哇,非常感谢!源链接对于解释 self 实例的不同方面非常有用。
【解决方案3】:

1st - 即使在这里,类的使用也是相关的。 python中几乎所有的东西都是一个对象,即使你没有意识到它。 2nd - 类变量和实例变量存在差异。将课堂视为蓝图。每次您要基于类创建对象时都会访问此蓝图。在类内声明但在任何方法之外(在您的情况下,例如 pokedex)声明的变量称为类变量。这些包含蓝图的值。当您创建类(对象)的实例时,这些类变量将转换为对象的实例变量。实例变量在 init 和其他函数中定义,您可以使用 self.variable 引用任何实例变量。有很多关于类和实例的知识,并且不能用简单的堆栈溢出答案来理解。关注https://docs.python.org/3.6/tutorial/classes.html

看看你的代码,你做得很好。我会使用更多的“自我”:) 例如在 L1、L2 或计算上,因为永远不知道我什么时候想要在创建它们的方法之外引用它们。好的规则是,您认为将来可能需要的任何东西都可以在类中使用 self 进行引用:)

最后但同样重要的是:是的,您的程序可能没有任何自定义类。或者您不需要使用类或实例变量,而是将所有内容保存在全局变量和局部变量中。它也可能没有任何方法或函数 :) 并且可以用汇编编写 :D 有很多可能性,但是当您进行面向对象编程时,使用对象很好:) 现在您的类是可重用的,例如我可以将它从您的模块导入到我的程序中。 :) 即使您只从每个程序的类中实例化一个对象,您通常也使用类作为它的蓝图。单独理解整个代码及其部分也更容易。就像在现实世界中一样——我是 Peter,我是 Person 类的一个实例。我有一只右手,我称之为 self.right_hand(我的右手,而不仅仅是任何一只手),这只手是 Hand 类的一个实例。 Hand 类有一个方法抓取。我的右手也能抓住!我通过调用 self.right_hand.grab(apple) 用右手抓住一个苹果。

祝你进步!

【讨论】:

  • 谢谢!我现在意识到自己应该是​​我最好的新朋友:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-17
  • 2017-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-19
相关资源
最近更新 更多