为啥没有构造函数参数的类需要括号

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 = MainViewControllerclass 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++:调用无参数的构造函数为啥不加括号

为啥 C++ 映射类型参数在使用 [] 时需要一个空的构造函数?

默认构造函数,为啥我的类似乎有三个?当编译器将类视为结构时?

为啥 C# 3.0 对象初始值设定项构造函数括号是可选的?

Kotlin类与继承

为啥 C++ 构造函数在继承中需要默认参数?