Skip to content

Instantly share code, notes, and snippets.

@bunop
Created November 14, 2019 10:16
Show Gist options
  • Save bunop/9ef97d74992b384ca5e41a38338f9d07 to your computer and use it in GitHub Desktop.
Save bunop/9ef97d74992b384ca5e41a38338f9d07 to your computer and use it in GitHub Desktop.
Python mangling and private methods etude
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Nov 14 10:18:35 2019
@author: Paolo Cozzi <[email protected]>
An etude of private methods on python and name mangling. For references, a
recap of underscore naming convenctions:
https://dbader.org/blog/meaning-of-underscores-in-python
https://pep8.org/#naming-conventions
A reference to Dive Into Python for private methos, that is name mangling,
changing function name with a predefined pattern. Those functions are not
truly privates, you can access them using the mangling name convection:
https://linux.die.net/diveintopython/html/object_oriented_framework/private_functions.html
A nice example of a decorator to make a method private in python. More info at:
https://medium.com/@vishwa.efor/handy-python-features-e33751ef98a8
"""
import inspect
from functools import wraps
# https://medium.com/@vishwa.efor/handy-python-features-e33751ef98a8
class PrivateFunctionError(Exception):
def __init__(self):
super().__init__(
self,
"This is a private function and should not be called by outside")
def private_function(function):
@wraps(function)
def run_if_not_private(*args, **kwargs):
caller = inspect.stack()[1][4][0].strip()
print("Caller : {}".format(caller))
if ("self." + function.__name__) in caller:
return function(*args, **kwargs)
else:
raise PrivateFunctionError()
return run_if_not_private
class Parent(object):
def public(self):
print("I'm a Parent's public method")
def __private(self):
"""A method that will change is name using the name mangling"""
print("I'm a Parent's private method")
def __str__(self):
return "I'm a Parent's special method"
def _internal(self):
print("I'm a Parent's internal method")
def call_parent_private(self):
"""Calling private method"""
self.__private()
@private_function
def a_truly_private_method(self):
print("I'm a Parent's decorated private method")
@private_function
def __a_mangled_private_method(self):
print("I'm a Parent's mangled and decorated private method")
class Child(Parent):
def call_child_private(self):
"""Calling private method"""
self.__private()
if __name__ == "__main__":
# define parent
parent = Parent()
# calling private function through a public method
parent.call_parent_private()
# calling private function doesn't work, since the name mangling has
# renamed this function as _Parent__private
try:
parent.__private()
# 'Parent' object has no attribute '__private'
except AttributeError as e:
print(e)
# however private methods are masked using '_<class>__<function>'
# pattern attribute, so you could access private methods using this
# name convenction
parent._Parent__private()
# define child
child = Child()
# call parent private function through parent public method
child.call_parent_private()
# calling parent private function through child public method doesn't
# work since, child hasn't method __private due to the parent name mangling
try:
child.call_child_private()
# 'Child' object has no attribute '_Child__private'
except AttributeError as e:
print(e)
# however , you can always refer to the parent mangled function
child._Parent__private()
# now call function decorated with the @private_function
# this is a truly private method, however you can't hide this name
# from outside class namespace
try:
parent.a_truly_private_method()
# (PrivateFunctionError(...), 'This is a private function and should
# not be called by outside')
except PrivateFunctionError as e:
print(e)
# and this private function is inherited from the child class
try:
child.a_truly_private_method()
# (PrivateFunctionError(...), 'This is a private function and should
# not be called by outside')
except PrivateFunctionError as e:
print(e)
# what happen if I apply the @private_function decorator to a
# mangled fucntion? This method is not named like this
# 'Parent' object has no attribute '__a_mangled_private_method'
try:
parent.__a_mangled_private_method()
# 'Parent' object has no attribute '__private'
except AttributeError as e:
print(e)
# and the real name is not accessible from the outside
try:
parent._Parent__a_mangled_private_method()
# (PrivateFunctionError(...), 'This is a private function and should
# not be called by outside')
# (PrivateFunctionError(...), 'This is a private function and should
# not be called by outside')
except PrivateFunctionError as e:
print(e)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment