-1

Consider a class Employee

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

emp_obj = Employee('Sam', 50000)
print(emp_obj.name, emp_obj.salary)

I tried to implement encapsulation in Python to restrict access to important data like salary. For this, I declared the variable salary as private by placing two underscores as the prefix. Also, I modified the above code by referring the second answer from the following link from StackOverflow: StackOverflow Link

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.__salary = salary

    @property
    def salary(self):
        return self.__salary

    @salary.setter
    def salary(self, salary):
        self.__salary = salary

    def getSalary(self):
        return 'The Salary is {0}' .format(self.salary)

emp_obj = Employee('Sam', 50000)
print(emp_obj.name, emp_obj.salary)
print(emp_obj.getSalary())

I do not see any considerable difference.

So I modified the code again:

class Employee:
    def __init__(self, name):
        self.name = name
        #self.__salary = salary

    def setSalary(self, salary):
        self.__salary = salary

    def getSalary():
        return salary

emp_obj = Employee('Sam')
emp_obj.setSalary(50000)
print(emp_obj.name)
print(emp_obj.getSalary)

The output I received was:

Sam
<bound method Employee.getSalary of <__main__.Employee object at 0x0000019E1B961A58>>

What does the above output mean? Clearly, my code did not work properly. I tried to set the salary by passing the value of salary as an argument. Kindly specify the error and the solution to solve the issue.

I also have another doubt - What do Python functions(methods) that start and end with underscores represent or convey?

def __init__(self):

The above command is used to initialize constructor in Python, which is called by default when an object of a class is created.

I am using Python 3.6.5.

The code was changed as per @ShadowRanger:

print(emp_obj.getSalary())

Then I received the following output with an error:

Sam
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-21-f115117b2a1c> in <module>()
     13 emp_obj.setSalary(50000)
     14 print(emp_obj.name)
---> 15 print(emp_obj.getSalary())

TypeError: getSalary() takes 0 positional arguments but 1 was given
Bipin
  • 453
  • 7
  • 12
  • 2
    You forgot to call `getSalary` when you changed it from a property to a plain method. Add parentheses, it will work. Also, plain method accessors are not Pythonic in the least. – ShadowRanger Jan 25 '20 at 02:15
  • You should print emp_obj.getSalary() , you forget to add append () to call such function, thus print the function itself. Of design pattern / encapsulation, I believe it's not easy to get through with just one question :) – Jack Wu Jan 25 '20 at 02:19
  • 1
    There's no point in doing this. You say it's "to restrict access to important data like salary", but you haven't actually restricted access in any way. – user2357112 Jan 25 '20 at 02:23
  • @user2357112supportsMonica I tried by making the salary variable private. My query is how to do the encapsulation in the proper way in the scenario specified. – Bipin Jan 25 '20 at 02:27
  • The intention was to clear the doubt lingering in my mind, by specifying a simple example. – Bipin Jan 25 '20 at 02:28
  • 1
    But anyone can still access it, because you immediately put in `getSalary` and `setSalary` methods that provide the access you sought to restrict. (Also leading-double-underscore variables don't actually have any access restrictions; they just get their name automatically adjusted a bit.) – user2357112 Jan 25 '20 at 02:32
  • 1
    "I tried to implement encapsulation in Python to restrict access to important data like salary". There is no concept of private methods or variables in Python. Everything is accessible if you know the attributes. The __ prefix is intended to convey something is meant to only be used within the context of the class and is not meant for public consumption. – dlachasse Jan 25 '20 at 02:33
  • 1
    "declared the variable salary as private by placing two underscores as the prefix." **NO**. Double-underscore name-mangling is to prevent name-collisions in subclasses, it is **not** private. Python *doesn't have private attributes*. Note, your properties are totally pointless. Your class *should* look like the the first version you presented. By *convention* attributes that are not public parts of the API are prefixed with a single underscore. Note double. And you *definitely should never write getters and setters*, i.e. python classes should not have `getX` and `setX` for plain attributes – juanpa.arrivillaga Jan 25 '20 at 02:38
  • 1
    Note, you missed the whole point of the second answer, where it states: "You **wouldn't actually define these property functions so simply**, but a big benefit is that you can start by allowing direct access to a name attribute, and **if you decide later that there should be more logic involved in getting/setting the value and you want to switch to properties, you can do so without affecting existing code.**" The whole point of `property` is that it gives you *encapsulation* **without having to write boilerplate getters and setters at all** Again, the correct way to do this is your very first – juanpa.arrivillaga Jan 25 '20 at 02:42
  • Variable and function names should follow the `lower_case_with_underscores` style. I would **strongly recommend** learning more about Python, and programming in general. You seem to be unaware of some rather important Python concepts and conventions. Stack Overflow is not meant to be a substitute for guides and tutorials. – AMC Jan 25 '20 at 03:35

1 Answers1

1

To fix the issue with the print statement, you can do the following:

def getSalary(self):
   return ....

You had forgotten the self which is required for every instance method.

Perhaps a more common case might be someone's ID number, like a tax ID or social security number. In this case, you could use an obscuring algorithm:

class Person:
    def __init__(self):
        self._ssn = None

    @property
    def ssn(self):
        return f'{self._ssn[:3]}-XX-XXXX'

here all callers will see an obscured version of the SSN when accessing it using the property.

Using the __ will cause the behavior that you want with external callers not being able to access the variable directly, but there are ways that you can bypass that encapsulation.

2ps
  • 15,099
  • 2
  • 27
  • 47