为什么在类中不能将函数名与变量名命名为相同的名称?

3

我的代码目前看起来像这样:

class Employee:

    def __init__(self, first, last):
        self.first = first
        self.last = last
        self.email = first + '.' + last + '@email.com'

    def email(self):
        return '{}.{}@email.com'.format(self.first, self.last)

    def fullname(self):
        return '{} {}'.format(self.first, self.last)

emp_1 = Employee('John', 'Smith')

emp_1.first = 'Jim'

print(emp_1.first)
print(emp_1.email())
print(emp_1.fullname())

我输出的结果是:
Jim
Traceback (most recent call last):
  File "/home/djpoland/python-files/test.py", line 19, in <module>
    print(emp_1.email())
TypeError: 'str' object is not callable

我知道可以通过删除self.email或更改email函数的名称来解决此问题。但是,为什么变量名和函数不能具有相同的名称?这只是Python标准惯例还是出于某些内部原因导致此问题?我尝试在谷歌上搜索原因,但找不到任何信息。


你的问题标题有些误导性。你可以拥有一个同名的函数和变量——只是不能在同一命名空间中。你似乎在问为什么一个属性不能与同一对象的方法同名。答案(如我下面所述)是,方法只是一个值为函数的属性。 - BrenBarn
5个回答

4
因为在Python中,类成员的内部表示是一个字典__dict__,它包含所有方法和实例变量的名称。由于字典中的键需要是唯一的,它们不能相同。实际上,方法和变量共享相同的命名空间(请参阅下文)。
要精确地说,__dict__仅存储实例变量,仅当找不到给定的键时,才会向上搜索存储方法的类变量,该变量也被命名为__dict__
因此,如果您执行以下操作:
class Employee:

def __init__(self, first, last):
    self.first = first
    self.last = last

def email(self):
    return '{}.{}@email.com'.format(self.first, self.last)

def fullname(self):
    return '{} {}'.format(self.first, self.last)

emp1 = Employee("John","Doe")
print(emp1.__dict__)

您将获得{'first': 'John', 'last': 'Doe'}

因此,搜索会涉及到类变量:

 print(Employee.__dict__)

{'__module__': '__main__', '__init__': <function Employee.__init__ at 0x03933468>, 'email': <function Employee.email at 0x03933420>, 'fullname': <function Employee.fullname at 0x039333D8>, '__dict__': <attribute '__dict__' of 'Employee' objects>, '__weakref__': <attribute '__weakref__' of 'Employee' objects>, '__doc__': None}

如果找到键名为email的函数,则会执行该函数。

但是,如果你的类包含了以下字段:

class Employee:

def __init__(self, first, last):
    self.first = first
    self.last = last
    self.email = "ple@ple.pl"

def email(self):
    return '{}.{}@email.com'.format(self.first, self.last)

def fullname(self):
    return '{} {}'.format(self.first, self.last)

print(emp1)
{'first': 'John', 'last': 'Doe', 'email': 'ple@ple.ple'}

搜索停止

正如 @Max 指出的那样,如果方法具有相同的名称,则可以访问该方法。但是,应避免使用这种做法。我无法想象出一个例子,这种解决方案将是有效的。


请查看我的评论,尽管函数与实例变量同名,但您仍然可以访问该函数。 - Max

2
因为在Python中,方法只是一个“变量”(也就是实例变量,又称属性),其值是一个函数。

1
在Python中,函数被视为对象。虽然这可能看起来很烦人,但实际上非常有用,因为函数可以像任何其他对象一样传递。考虑以下内容:
def func(x):
    print 'Func executed'

def run_func(f):
   f()

run_func(func)

输出: 函数执行 在这个例子中,当func被传递到函数run_func中时,它被视为一个对象。请注意,func并没有直接调用:即,代码行是run_func(func)而不是run_func(func())
可以想象,这有很多非常有用的应用程序。

1

这不是问题。根据理论,一个对象不能有两个相似的成员名称。因此,由于Python是解释性语言,当你到达声明函数的那一行时,它会给出一个错误。如果是C#或Java,编译时就会出现错误。

所以这必须是一个概念主题。任何支持面向对象范式的语言都会给出相同的错误。


你可以在同一个类中编写两个同名的方法,但是你将无法同时使用它们(至少在Python中是这样,尽管我猜其他语言也是如此),因为它们会相互覆盖。 - Max

1
“本质上,方法和变量共享相同的命名空间”这并不完全正确。事实上,即使你有一个名为email的实例变量,你仍然可以访问email函数,如何做到呢?使用emp_1.__class__.__dict__ ["email"](emp_1)
那么到底发生了什么呢? 在Python中,实例变量位于字典中,存储在类实例的__dict__变量中。但是,还有另一个字典,它保存了有关类实例的更多信息,包括__dict__对象。
当使用点运算符(.)访问类中的对象时,Python首先搜索实例变量(即__dict__对象),这就解释了为什么当您尝试在函数周围加括号时,它会失败并抛出错误str object is not callable,因为您试图调用找到的名为email的实例变量并将其调用。
如果Python在instance.__dict__中未找到与点操作后面的名称匹配的任何内容,它将开始在instance.__class__.__dict__中搜索,尝试找到与名称匹配的方法。
另一个澄清是,两个具有相同名称的方法是不可能的,因为如上所述,这些方法存储在字典中,在instance.__class__.__dict__中,因此它们必须是唯一的。如果您尝试这样做,它将只覆盖其中一个,取决于顺序。
如果有什么不清楚的,请告诉我,我会给出几个例子。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接