Reputation: 1043
My code currently looks like:
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())
And my output gives:
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
I understand that I can fix this issue by removing self.email or by changing the name of the email function. However, why can't a variable name and a function have the same name? Is this just a Python standard convention or is there an internal reason for this issue? I tried googling for why but I can't find any information.
Upvotes: 3
Views: 1049
Reputation: 3355
Because in python the internal representation of class members is a dictionary __dict__
and it contains the names of all the methods and instance variables. And because the keys in the dictionary need to be unique they cannot be same. Essentially methods and variable share the same namespace (kind of read below)
To be precise __dict__
stores instance variables, only if a given key is not found it goes up to search class variables where the methods are stored also in a variable named __dict__
.
So if you do:
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__)
You will get {'first': 'John', 'last': 'Doe'}
So the search goes up to class varaibles:
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}
Where it finds the key email
assigned to a function and invokes it.
But if your class contains that field:
class Employee:
def __init__(self, first, last):
self.first = first
self.last = last
self.email = "[email protected]"
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': '[email protected]'}
And the search stops
As @Max pointed out there is a possibility to access the method if it has the same name. However,such practices should be avoided. I cannot think of an example where such a solution would be valid.
Upvotes: 4
Reputation: 1175
"Essentially methods and variable share the same namespace" this is not entirly true.
The thing is, you can access the email
function even though you have an instace variable with the name email
, how?
use emp_1.__class__.__dict__["email"](emp_1)
.
So what exactly happened there?
In python, instance variable are located in a dict as you said, stored in the variable __dict__
of the class instance.
BUT there is another dictionary, which holds of more information about the class instance, INCLUDING the __dict__
object.
When accessing an object in a class with the dot operator (.
), python first searches the instance variables (the __dict__
object), that explains why when you tried to put braces around the function.
It failed throwing an error str object is not callable
because you tried to call the instance variable that it found under the name email
and call it.
If python does not find the anything in matching the name of what comes after the dot operation in instance.__dict__
, it starts to search in instance.__class__.__dict__
trying to find a method matching the name.
Another clarification, two methods with the same name is not possible, since as said above, the methods are stored in a dictionary, in instance.__class__.__dict__
, thus they have to be unique.
If you try to do that, it will just override one of them, depends on the order.
If something is not clear, tell me i will give few examples.
Upvotes: 1
Reputation: 11
It is not an issue. According to the theory, an object can not have two members with a similar name. then as Python is interpreted it gives you an error when you get to the line where you declare the function. If it were C # or java, I would have given you an error when compiling.
So it has to be a conceptual theme. the same error gives you in any language that supports the paradigm to objects.
Upvotes: 1
Reputation: 294
In python, functions are treated as objects. Although this might seem annoying, it's actually incredibly useful because functions can be passed around just like any other object. Consider the following:
def func(x):
print 'Func executed'
def run_func(f):
f()
run_func(func)
Output: Func executed
In this example, func
was treated as an object when it was passed into the function run_func
. Note that func
isn't called directly: I.e., the line is run_func(func)
not run_func(func())
.
As you can imagine, there are many incredibly useful applications for this.
Upvotes: 1
Reputation: 251363
Because in Python, a method is just a "variable" (that is, an instance variable, aka an attribute) whose value is a function.
Upvotes: 2