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

tom*_*web 1 python oop tkinter

我开始学习PHP中的编程/ OOP。据我对PHP最佳实践的了解,如果不带任何参数,则可以实例化没有括号的类。

如

$class = new Class;
Run Code Online (Sandbox Code Playgroud)

相对于:

$class = new Class();
Run Code Online (Sandbox Code Playgroud)

我开始将自己的技能扩展到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
Run Code Online (Sandbox Code Playgroud)

有人可以帮我理解为什么在Python中,即使没有以外的构造函数参数,也需要使用括号实例化一个类self。另外,为什么还要做MainViewController这种工作,但是却没有达到我想要的表现?就像在其中加载了函数一样,函数“完成了事情”,但似乎不接受参数。实例化没有括号的类有什么好处?

请注意,我不需要帮助来使此代码正常工作,我只想了解为什么会发生这种情况。

Ric*_*ica 5

有人可以帮我理解为什么在Python中,即使self之外没有构造函数参数,也需要使用括号实例化一个类。

原因很简单:实例化对象时,实际上是在调用它的类(它本身是对象),然后使用调用对象()。

在python中,所有东西都是一流的对象,甚至类(和函数!)本身也是如此。为了使一个类成为第一类对象,需要遵循以下条件:该类需要使用自己的类(metaclass)来定义其行为。我们将类的类称为“元类”,以避免在谈论类和类的类时产生混淆。

回答问题的第二部分:当您使用MainViewController而不是MainViewController()因为它MainViewController 是一个成熟的对象(就像其他任何对象一样)时,发生了“事情” 。

因此,您可能会问:MainViewController对象的类是什么(实际上是元类)?


如您所知,您可以创建这样的类:

class MyClass: 
    pass
Run Code Online (Sandbox Code Playgroud)

执行此操作时,实际上是在创建称为type的元类的新实例。

注意,您可以通过这种方式创建相同的类。上面和下面之间没有什么区别:

MyClass = type('MyClass', (object,), {}) 
Run Code Online (Sandbox Code Playgroud)

该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)
Run Code Online (Sandbox Code Playgroud)

有趣的是,type它甚至是它自己的元类:

print(type(type)) # type
Run Code Online (Sandbox Code Playgroud)

重申一下:该类MyClass实际上是的实例化type。随之而来的是,调用该类将导致运行__call__其元类的方法。

当您这样做时:

obj = MyClass()
Run Code Online (Sandbox Code Playgroud)

...您正在调用 MyClass,导致(在后台)运行method type.__call__()。

所有用户定义的类都是这种情况,btw; 如果__call__在类中包含该方法,则该类是可调用的,并且在__call__调用类实例时将执行该方法:

class MyCallable():
    def __call__(self):
        print("You rang?") 

my_instance = MyCallable()
my_instance() # You rang?
Run Code Online (Sandbox Code Playgroud)

您可以看到此操作。如果通过子类创建自己的元类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
Run Code Online (Sandbox Code Playgroud)

现在,当您这样做时MyClass(),您可以看到发生的__call__方法MyMeta先于其他任何事物(包括__new__AND和before __init__)。