1

I know that most of You stays for that exec shouldn't be used, but I have some problem.

Here is minimal example, which works:

class A:
    def __init__(self):
        exec('self.a = self.funct()')
    def funct(self):
        return 1
    def ret(self):
        return self.a
> obj = A()
> obj.ret()
1

But, when I do:

class A:
    def __init__(self):
        exec('self.a = self.__funct()')
    def __funct(self):
        return 1
    def ret(self):
        return self.a
> obj = A()
AttributeError: 'A' has no attribute '__funct'

Does anybody know why is that difference?

Koral
  • 80
  • 6

2 Answers2

3

__name names are class private; such names are prefixed, at compile time, with another underscore and the class name. The purpose is to protect the names from accidental clashes with names used in subclasses. These names are not meant to be private to outside callers.

Quoting the Reserved classes of identifiers section:

__*
Class-private names. Names in this category, when used within the context of a class definition, are re-written to use a mangled form to help avoid name clashes between “private” attributes of base and derived classes.

and the Identifiers (Names) section:

Private name mangling: When an identifier that textually occurs in a class definition begins with two or more underscore characters and does not end in two or more underscores, it is considered a private name of that class. Private names are transformed to a longer form before code is generated for them. The transformation inserts the class name, with leading underscores removed and a single underscore inserted, in front of the name. For example, the identifier __spam occurring in a class named Ham will be transformed to _Ham__spam. This transformation is independent of the syntactical context in which the identifier is used.

What happens in your case is that exec() postpones compilation, effectively compiling that call in isolation. The class context is gone, so no mangling takes place.

As such, you need to apply the automatic prefixing manually:

exec('self.a = self._A__funct()')

If you are using Python 3, you could use the __class__ closure normally available for the super() function to access the class name the current method is defined for:

exec('self.a = self._{0.__name__}__funct()'.format(__class__))

Now, unless you actually plan for your class to be widely subclassed in third-party code that should not have to worry about accidentally clashing with internal implementation details, you should not be using double-underscore names at all. Stick with single-underscore names instead.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
0

Python private methods are "disguised" under a different identifier than the one specified in the code, you can access them like _classname__privateAttribute.

Posting this to be specific:

class A:
    def __init__(self):
        exec('self.a = self._A__funct()')
    def __funct(self):
        print("Hello")
    def ret(self):
        return self.a

obj = A()

I put the print answer there to detect if it worked, and it did!

David Černý
  • 112
  • 2
  • 11