构造函数外部方法中的实例变量(Python)——为什么以及如何?
Instance variables in methods outside the constructor (Python) -- why and how?
我的问题涉及 在 class 构造函数 之外的方法中初始化的实例变量。这是为了 Python。
我先说一下我的理解:
- 类可以定义构造函数,也可以定义其他方法
- 实例变量一般在defined/initialized构造函数中
- 但是实例变量也可以在defined/initialized构造函数之外,例如在相同的其他方法中 class。
(2) 和 (3) 的示例 -- 参见 self.meow 和 self.roar在目录class下面:
class Cat():
def __init__(self):
self.meow = "Meow!"
def meow_bigger(self):
self.roar = "Roar!"
我的问题:
为什么最好在构造函数中初始化实例变量?
如果在构造函数以外的方法中定期初始化实例变量,会出现什么general/specific混乱? (例如,在他的编程 Python 中阅读了 Mark Lutz 的 Tkinter 指南,我认为它非常好,我注意到用于保存 PhotoImage objects/references 的实例变量是在进一步的方法中初始化的,而不是在构造函数中. 它似乎在那里没有问题地工作,但是这种做法会导致长时间的问题吗 运行?)
在什么情况下更好在其他方法中而不是在构造函数中初始化实例变量?
据我所知,实例变量不在创建 class 对象时存在,而是 在 实例化 class 对象之后.继续我上面的代码,我证明了这一点:
>> c = Cat()
>> c.meow
'Meow!'
>> c.roar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Cat' object has no attribute 'roar'
>>> c.meow_bigger()
>>> c.roar
'Roar!'
原样:
- 我一开始无法访问实例变量(c.roar)。
- 然而,当我调用实例方法c.meow_bigger()一次后,突然可以访问实例变量c.roar。
- 为什么会出现上述行为?
感谢您帮助我理解。
请记住 "pure" Python 中的 class 成员只是一个字典。在您 运行 定义成员的函数之前,成员不会添加到实例的字典中。理想情况下,这是构造函数,因为这样可以保证您的成员将全部存在,而不管您的函数被调用的顺序如何。
我相信你上面的例子可以翻译成:
class Cat():
def __init__(self):
self.__dict__['meow'] = "Meow!"
def meow_bigger(self):
self.__dict__['roar'] = "Roar!"
>>> c = Cat() # c.__dict__ = { 'meow': "Meow!" }
>>> c.meow_bigger() # c.__dict__ = { 'meow': "Meow!", 'roar': "Roar!" }
在构造函数中初始化实例变量,正如您已经指出的那样,仅在 python 中推荐。
首先,在构造函数中定义所有实例变量是 记录 class 的好方法。看到代码的每个人都知道实例具有什么样的内部状态。
其次,订单很重要。如果在函数 A
中定义了一个实例变量 V
,并且还有另一个函数 B 也在访问 V
,那么在 B
之前调用 A
很重要。否则 B
将失败,因为 V 从未被定义。也许,A
必须在 B
之前调用,但它应该由一个内部状态来确保,这将是一个实例变量。
还有很多例子。一般来说,在 __init__
方法中定义所有内容是个好主意,如果不能/不应该在初始化时将其初始化,则将其设置为 None
。
当然可以用hasattr
的方法推导出一些状态信息。但是,也可以检查某个实例变量 V
是否是例如 None,这可能意味着相同。
所以在我的 意见 中,像在构造函数中那样在其他任何地方定义实例变量从来都不是一个好主意。
您的示例说明了 python 的一些基本属性。 Python 中的对象基本上只是一个字典。
让我们使用字典:可以向该字典添加函数和值并构建某种 OOP。使用 class 语句只会将所有内容带入一个干净的语法中,并提供额外的东西,如 magic methods.
在其他语言中,有关实例变量和函数的所有信息在对象初始化之前就已存在。 Python 在运行时执行此操作。您还可以向 class 定义之外的任何对象添加新方法:Adding a Method to an Existing Object Instance
3.) But instance variables can also be defined/initialized outside the constructor, e.g. in the other methods of the same class.
我建议在初始化时提供一个默认状态,这样就清楚 class 应该期待什么。在静态类型语言中,您必须这样做,这是 python.
中的好习惯
让我们将变量 roar
替换为更有意义的变量,例如 has_roared
。
在这种情况下,您的 meow_bigger()
方法现在有理由设置 has_roar
。你会在 __init__
中将它初始化为 false,因为猫在实例化时还没有咆哮。
class Cat():
def __init__(self):
self.meow = "Meow!"
self.has_roared = False
def meow_bigger(self):
print self.meow + "!!!"
self.has_roared = True
现在你明白为什么用默认值初始化属性通常有意义了吗?
综上所述,为什么 python 不强制要求我们必须在 __init__
方法中定义变量?好吧,作为一种动态语言,我们现在可以做这样的事情了。
>>> cat1 = Cat()
>>> cat2 = Cat()
>>> cat1.name = "steve"
>>> cat2.name = "sarah"
>>> print cat1.name
... "steve"
name
属性未在 __init__
方法中定义,但我们仍然可以添加它。这是在 __init__
.
中设置非默认变量的更现实的用例
Why is it best practice to initialize the instance variable within the
constructor?
清晰度。
因为这样可以很容易地一目了然地看到class的所有属性。如果在多个方法中初始化变量,不阅读每一行代码就很难理解完整的数据结构。
在 __init__
中初始化也使文档更容易。用你的例子,你不能写"an instance of Cat has a roar
attribute"。相反,您必须添加一段说明 Cat 的实例可能具有 "roar" 属性,但仅在调用 "meow_louder" 方法之后。
清晰为王。我见过的最聪明的程序员之一曾经告诉我 "show me your data structures, and I can tell you how your code works without seeing any of your code"。虽然这有点夸张,但绝对有道理。学习代码库的最大障碍之一是理解它操作的数据。
What general/specific mess could arise if instance variables are
regularly initialized in methods other than the constructor?
最明显的一个是对象可能在程序的所有部分都没有可用的属性,导致必须添加大量额外的代码来处理未定义属性的情况。
In what scenarios would it be better to initialize instance variables
in the other methods, rather than in the constructor?
我认为没有。
注意:您不一定要用属性的最终值来初始化它。在您的情况下,可以将 roar
初始化为 None
。它已被初始化为 something 这一事实表明它是 class 维护的一条数据。如果值稍后发生变化也没关系。
我试着提供一个你会这样做的案例:
3.) 但是实例变量也可以 defined/initialized 在构造函数之外,例如在相同的其他方法中 class.
我同意在构造函数中包含实例字段会很清楚和有条理,但有时你会继承其他人 class,它是由其他人创建的,有很多实例字段和 api.
但是如果你只为某些apis继承它并且你想为你自己的apis拥有自己的实例字段,在这种情况下,你更容易声明方法中的额外实例字段代替了其他构造函数,而无需深入研究源代码。这也支持 Adam Hughes 的回答,因为在这种情况下,您将始终拥有定义的实例,因为您将保证先调用自己的 api。
例如,假设您继承了一个包的处理程序 class 用于 Web 开发,您希望为处理程序包含一个名为 user 的新实例字段,您可能只是直接在方法中声明它--initialize而不覆盖构造函数,我看到这样做更常见。
class BlogHandler(webapp2.RequestHandler):
def initialize(self, *a, **kw):
webapp2.RequestHandler.initialize(self, *a, **kw)
uid = self.read_cookie('user_id') #get user_id by read cookie in the browser
self.user = User.by_id(int(uid)) #run query in data base find the user and return user
我认为为了简单易懂,最好在 class 构造函数中初始化 class 变量,这样可以直接调用它们而无需编译特定的 class 方法.
class Cat():
def __init__(self,Meow,Roar):
self.meow = Meow
self.roar = Roar
def meow_bigger(self):
return self.roar
def mix(self):
return self.meow+self.roar
c=Cat("Meow!","Roar!")
print(c.meow_bigger())
print(c.mix())
输出
吼!
吼!
喵!吼!
这些都是非常开放的问题。
Python 是一种非常 "free" 的语言,因为它试图从不限制您做任何事情,即使它看起来很傻。这就是为什么您可以做完全无用的事情,例如用布尔值替换 class(是的,您可以)。
您提到的行为遵循相同的逻辑:如果您希望动态地将属性添加到对象(或函数 - 是的,您也可以),在任何地方,不一定在构造函数中,嗯.. .你可以。
但这并不是因为你可以,所以你应该。在构造函数中初始化属性的主要原因是可读性,这是维护的前提。由于 、class 字段是理解代码的关键,因为它们的名称和类型通常比方法更能揭示意图。
也就是说,现在有一种方法可以将属性定义与构造函数初始化分开:pyfields
。我编写这个库是为了能够根据属性定义 class 的 "contract",同时不需要在构造函数中进行初始化。这允许您特别创建 "mix-in classes",其中定义了属性和依赖于这些属性的方法,但没有提供构造函数。
有关示例和详细信息,请参阅 。
我的问题涉及 在 class 构造函数 之外的方法中初始化的实例变量。这是为了 Python。
我先说一下我的理解:
- 类可以定义构造函数,也可以定义其他方法
- 实例变量一般在defined/initialized构造函数中
- 但是实例变量也可以在defined/initialized构造函数之外,例如在相同的其他方法中 class。
(2) 和 (3) 的示例 -- 参见 self.meow 和 self.roar在目录class下面:
class Cat(): def __init__(self): self.meow = "Meow!" def meow_bigger(self): self.roar = "Roar!"
我的问题:
为什么最好在构造函数中初始化实例变量?
如果在构造函数以外的方法中定期初始化实例变量,会出现什么general/specific混乱? (例如,在他的编程 Python 中阅读了 Mark Lutz 的 Tkinter 指南,我认为它非常好,我注意到用于保存 PhotoImage objects/references 的实例变量是在进一步的方法中初始化的,而不是在构造函数中. 它似乎在那里没有问题地工作,但是这种做法会导致长时间的问题吗 运行?)
在什么情况下更好在其他方法中而不是在构造函数中初始化实例变量?
据我所知,实例变量不在创建 class 对象时存在,而是 在 实例化 class 对象之后.继续我上面的代码,我证明了这一点:
>> c = Cat() >> c.meow 'Meow!' >> c.roar Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Cat' object has no attribute 'roar' >>> c.meow_bigger() >>> c.roar 'Roar!'
原样:
- 我一开始无法访问实例变量(c.roar)。
- 然而,当我调用实例方法c.meow_bigger()一次后,突然可以访问实例变量c.roar。
- 为什么会出现上述行为?
感谢您帮助我理解。
请记住 "pure" Python 中的 class 成员只是一个字典。在您 运行 定义成员的函数之前,成员不会添加到实例的字典中。理想情况下,这是构造函数,因为这样可以保证您的成员将全部存在,而不管您的函数被调用的顺序如何。
我相信你上面的例子可以翻译成:
class Cat():
def __init__(self):
self.__dict__['meow'] = "Meow!"
def meow_bigger(self):
self.__dict__['roar'] = "Roar!"
>>> c = Cat() # c.__dict__ = { 'meow': "Meow!" }
>>> c.meow_bigger() # c.__dict__ = { 'meow': "Meow!", 'roar': "Roar!" }
在构造函数中初始化实例变量,正如您已经指出的那样,仅在 python 中推荐。
首先,在构造函数中定义所有实例变量是 记录 class 的好方法。看到代码的每个人都知道实例具有什么样的内部状态。
其次,订单很重要。如果在函数 A
中定义了一个实例变量 V
,并且还有另一个函数 B 也在访问 V
,那么在 B
之前调用 A
很重要。否则 B
将失败,因为 V 从未被定义。也许,A
必须在 B
之前调用,但它应该由一个内部状态来确保,这将是一个实例变量。
还有很多例子。一般来说,在 __init__
方法中定义所有内容是个好主意,如果不能/不应该在初始化时将其初始化,则将其设置为 None
。
当然可以用hasattr
的方法推导出一些状态信息。但是,也可以检查某个实例变量 V
是否是例如 None,这可能意味着相同。
所以在我的 意见 中,像在构造函数中那样在其他任何地方定义实例变量从来都不是一个好主意。
您的示例说明了 python 的一些基本属性。 Python 中的对象基本上只是一个字典。 让我们使用字典:可以向该字典添加函数和值并构建某种 OOP。使用 class 语句只会将所有内容带入一个干净的语法中,并提供额外的东西,如 magic methods.
在其他语言中,有关实例变量和函数的所有信息在对象初始化之前就已存在。 Python 在运行时执行此操作。您还可以向 class 定义之外的任何对象添加新方法:Adding a Method to an Existing Object Instance
3.) But instance variables can also be defined/initialized outside the constructor, e.g. in the other methods of the same class.
我建议在初始化时提供一个默认状态,这样就清楚 class 应该期待什么。在静态类型语言中,您必须这样做,这是 python.
中的好习惯让我们将变量 roar
替换为更有意义的变量,例如 has_roared
。
在这种情况下,您的 meow_bigger()
方法现在有理由设置 has_roar
。你会在 __init__
中将它初始化为 false,因为猫在实例化时还没有咆哮。
class Cat():
def __init__(self):
self.meow = "Meow!"
self.has_roared = False
def meow_bigger(self):
print self.meow + "!!!"
self.has_roared = True
现在你明白为什么用默认值初始化属性通常有意义了吗?
综上所述,为什么 python 不强制要求我们必须在 __init__
方法中定义变量?好吧,作为一种动态语言,我们现在可以做这样的事情了。
>>> cat1 = Cat()
>>> cat2 = Cat()
>>> cat1.name = "steve"
>>> cat2.name = "sarah"
>>> print cat1.name
... "steve"
name
属性未在 __init__
方法中定义,但我们仍然可以添加它。这是在 __init__
.
Why is it best practice to initialize the instance variable within the constructor?
清晰度。
因为这样可以很容易地一目了然地看到class的所有属性。如果在多个方法中初始化变量,不阅读每一行代码就很难理解完整的数据结构。
在 __init__
中初始化也使文档更容易。用你的例子,你不能写"an instance of Cat has a roar
attribute"。相反,您必须添加一段说明 Cat 的实例可能具有 "roar" 属性,但仅在调用 "meow_louder" 方法之后。
清晰为王。我见过的最聪明的程序员之一曾经告诉我 "show me your data structures, and I can tell you how your code works without seeing any of your code"。虽然这有点夸张,但绝对有道理。学习代码库的最大障碍之一是理解它操作的数据。
What general/specific mess could arise if instance variables are regularly initialized in methods other than the constructor?
最明显的一个是对象可能在程序的所有部分都没有可用的属性,导致必须添加大量额外的代码来处理未定义属性的情况。
In what scenarios would it be better to initialize instance variables in the other methods, rather than in the constructor?
我认为没有。
注意:您不一定要用属性的最终值来初始化它。在您的情况下,可以将 roar
初始化为 None
。它已被初始化为 something 这一事实表明它是 class 维护的一条数据。如果值稍后发生变化也没关系。
我试着提供一个你会这样做的案例:
3.) 但是实例变量也可以 defined/initialized 在构造函数之外,例如在相同的其他方法中 class.
我同意在构造函数中包含实例字段会很清楚和有条理,但有时你会继承其他人 class,它是由其他人创建的,有很多实例字段和 api.
但是如果你只为某些apis继承它并且你想为你自己的apis拥有自己的实例字段,在这种情况下,你更容易声明方法中的额外实例字段代替了其他构造函数,而无需深入研究源代码。这也支持 Adam Hughes 的回答,因为在这种情况下,您将始终拥有定义的实例,因为您将保证先调用自己的 api。
例如,假设您继承了一个包的处理程序 class 用于 Web 开发,您希望为处理程序包含一个名为 user 的新实例字段,您可能只是直接在方法中声明它--initialize而不覆盖构造函数,我看到这样做更常见。
class BlogHandler(webapp2.RequestHandler):
def initialize(self, *a, **kw):
webapp2.RequestHandler.initialize(self, *a, **kw)
uid = self.read_cookie('user_id') #get user_id by read cookie in the browser
self.user = User.by_id(int(uid)) #run query in data base find the user and return user
我认为为了简单易懂,最好在 class 构造函数中初始化 class 变量,这样可以直接调用它们而无需编译特定的 class 方法.
class Cat():
def __init__(self,Meow,Roar):
self.meow = Meow
self.roar = Roar
def meow_bigger(self):
return self.roar
def mix(self):
return self.meow+self.roar
c=Cat("Meow!","Roar!")
print(c.meow_bigger())
print(c.mix())
输出
吼!
吼!
喵!吼!
这些都是非常开放的问题。
Python 是一种非常 "free" 的语言,因为它试图从不限制您做任何事情,即使它看起来很傻。这就是为什么您可以做完全无用的事情,例如用布尔值替换 class(是的,您可以)。
您提到的行为遵循相同的逻辑:如果您希望动态地将属性添加到对象(或函数 - 是的,您也可以),在任何地方,不一定在构造函数中,嗯.. .你可以。
但这并不是因为你可以,所以你应该。在构造函数中初始化属性的主要原因是可读性,这是维护的前提。由于
也就是说,现在有一种方法可以将属性定义与构造函数初始化分开:pyfields
。我编写这个库是为了能够根据属性定义 class 的 "contract",同时不需要在构造函数中进行初始化。这允许您特别创建 "mix-in classes",其中定义了属性和依赖于这些属性的方法,但没有提供构造函数。
有关示例和详细信息,请参阅