为啥没有构造函数参数的类需要括号
Posted
技术标签:
【中文标题】为啥没有构造函数参数的类需要括号【英文标题】:Why do classes with no constructor arguments need parenthesis为什么没有构造函数参数的类需要括号 【发布时间】:2015-10-03 04:43:36 【问题描述】:我开始学习 php 中的编程/OOP。据我对 PHP 最佳实践的了解,如果一个类不带任何参数,您可以实例化一个不带括号的类。
如
$class = new Class;
相对于:
$class = new Class();
我开始将我的技能扩展到 python 并且昨天浪费了大约 5 个小时试图弄清楚为什么一个函数不会传递参数,即使它非常简单。我的代码:
class MainViewWidgets(MainViewContainer):
def __init__(self):
# instantiating like this prevents MainViewController.getHeaderItems from returning the arg passed to it, however the code still "works" in some sense
self.controller = MainViewController
#this works
self.controller = MainViewController()
def createHeaderOptionCheckbox(self, pane):
self.header_string = StringVar()
header_checkbox = ttk.Checkbutton(pane, text='Data Contains Headers', variable=self.header_string, onvalue='headers', offvalue='keys')
self.header_string.trace('w', self.headerOptionCheckboxChanged)
return header_checkbox
def headerOptionCheckboxChanged(self, *args):
print(self.header_string.get())
#will print "headers" or "keys" on checkbox toggle
print(self.controller.getHeaderItems(self.header_string.get()))
#prints "default"
class MainViewController:
def __init__(self):
self.CheckFile = CheckFile()
get_config = GetConfiguration('config.ini')
self.config_file = get_config.getProperty('directory', 'input_file')
self.csv = CSVReader(self.config_file)
self.chosen_index = None
def getHeaderItems(self, header='default'):
return header
谁能帮我理解为什么在 Python 中你需要用括号实例化一个类,即使除了self
之外没有构造函数参数。另外,为什么MainViewController
仍然可以工作,但它的行为不像我想要的那样?就像它被加载一样,函数“做了一些事情”,但它似乎不接受参数。实例化一个没有括号的类有什么好处吗?
请注意,我不需要帮助来让这段代码工作,我只是想了解为什么会发生这种情况。
【问题讨论】:
因为它是 Python 语法的定义方式。 试试merp = MainViewController; print(merp())
:P
总而言之,您可以像传递类实例一样传递类。所以self.controller = MainViewController
将class
MainViewController 分配给self.controller。如果你想要一个实例,你需要括号。
谢谢你,这绝对让我明白了。
你甚至可以用pythonget real meta
【参考方案1】:
有人能帮我理解为什么在 Python 中你需要用括号实例化一个类,即使除了 self 之外没有构造函数参数。
原因很简单:当您实例化一个对象时,您实际上是在调用它的类(它本身就是一个对象),并且您使用()
调用对象。
在 python 中,一切都是一流的对象,甚至类(和函数!)本身也是如此。为了使一个类成为第一类对象,该类需要它自己的类 (metaclass) 来定义其行为。我们将类的类称为“元类”,以免在谈论类和类的类时产生混淆。
回答您问题的第二部分:当您使用 MainViewController
而不是 MainViewController()
时,“事情”正在发生,因为 MainViewController
是一个成熟的对象,就像任何其他对象一样目的。
所以你可能会问:MainViewController
对象的类——实际上是元类——是什么?
如你所知,你可以像这样创建一个类:
class MyClass:
pass
执行此操作时,实际上是在创建metaclass known as type
的新实例。
请注意,您可以通过这种方式创建相同的类;从字面上看,下面和上面没有区别:
MyClass = type('MyClass', (object,), )
type
元类是所有类的基础元类。所有python“新样式类”(我相信它们是在python 2.1中实现的,不再那么“新”了)属于type
类:
print(type(MyClass)) # type
print(type(list)) # type
print(type(int)) # type
# Note that above, type is being used as a "function" (it's really just a callable)
有趣的是,type
甚至是它自己的元类:
print(type(type)) # type
所以重申一下:MyClass
类实际上是type
的实例化。因此,调用类会导致运行其元类的__call__
方法。
当你这样做时:
obj = MyClass()
...您正在调用MyClass
,这会导致(在后台)运行type.__call__()
方法。
这是所有用户定义的类的情况,顺便说一句;如果你在你的类中包含__call__
方法,你的类是可调用的,当你调用类实例时会执行__call__
方法:
class MyCallable():
def __call__(self):
print("You rang?")
my_instance = MyCallable()
my_instance() # You rang?
您可以看到这一点。如果您通过子类化type
创建自己的元类,则在创建基于您的自定义元类的类的实例时可能会发生一些事情。例如:
class MyMeta(type):
def __call__(self, *args, **kwargs):
print "call: ".format(self, args, kwargs)
return super().__call__(*args, **kwargs)
# Python 3:
class MyClass(metaclass = MyMeta):
pass
# Python 2:
class MyClass():
__metaclass__ = MyMeta
pass
现在当您执行MyClass()
时,您可以看到MyMeta
的__call__
方法发生在其他任何事情之前(包括在__new__
之前和__init__
之前)。
【讨论】:
这是真的,而且内容丰富,但不是真正的答案。 我选择关注第一个问题,因为它似乎是主要问题。【参考方案2】:因为函数调用需要()
。当您执行MyClass()
时,您正在调用MyClass
。表达式 MyClass
的计算结果是类本身,它是一个对象。
【讨论】:
以上是关于为啥没有构造函数参数的类需要括号的主要内容,如果未能解决你的问题,请参考以下文章
为啥 C++ 映射类型参数在使用 [] 时需要一个空的构造函数?