Gangmax Blog

Python notes 2: underscores, dunder functions and dynamic creations

underscores

From here and here.

the following special forms using leading or trailing underscores are recognized (these can generally be combined with any case convention):

  • _single_leading_underscore: weak “internal use” indicator. E.g. “from M import *“ does not import objects whose name starts with an underscore.

  • single_trailing_underscore_: used by convention to avoid conflicts with Python keyword, e.g.

1
Tkinter.Toplevel(master, class_='ClassName')
  • __double_leading_underscore: when naming a class attribute, invokes name mangling (inside class FooBar, __boo becomes _FooBar__boo). If your class is intended to be subclassed, and you have attributes that you do not want subclasses to use, consider naming them with double leading underscores and no trailing underscores. This invokes Python’s name mangling algorithm, where the name of the class is mangled into the attribute name. This helps avoid attribute name collisions should subclasses inadvertently contain attributes with the same name.

Note 1: Note that only the simple class name is used in the mangled name, so if a subclass chooses both the same class name and attribute name, you can still get name collisions.

Note 2: Name mangling can make certain uses, such as debugging and __getattr__() , less convenient. However the name mangling algorithm is well documented and easy to perform manually.

Note 3: Not everyone likes name mangling. Try to balance the need to avoid accidental name clashes with potential use by advanced callers.

  • __double_leading_and_trailing_underscore__: “magic” objects or attributes that live in user-controlled namespaces. E.g. __init__, __import__ or __file__. Never invent such names; only use them as documented.

Note that names with double leading and trailing underscores are essentially reserved for Python itself: “Never invent such names; only use them as documented”.

Dunder functions

from here and here.

Another term of “dunder functions” is “magic methods”, they mean the ones with “__double_leading_and_trailing_underscore__“. here is a detail document about this topic.

Here are the rules you should follow for dunders:

  1. Call them “dunders” — Terminology like “magic” makes them seem much more complicated than they actually are. For example, it’s the “dunder call method”, not the “double-underscore call double-underscore method” and not the “magic call method”.

  2. Implement dunders on your classes at will — There is nothing magic about them, you shouldn’t feel like you’re using an esoteric language feature when you implement the __call__ method. Because, you’re not! It’s a standard language feature just like __init__!

  3. Never, ever, invent your own dunders — Python leaves you with a number of clean namespaces (classes, modules, etc.) for your own code. Use them! The core Python team reserved a somewhat ugly namespace for themselves — don’t trample all over their compromise by stealing their names.

Dynamic Creations

  • How to create a module?

From here and here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> import imp
>>> foo = imp.new_module("foo")
>>> foo_code = """
... class Foo:
... pass
... """
>>> exec foo_code in foo.__dict__
>>> foo.Foo.__module__
'foo'
>>> import sys
>>> sys.modules["foo"] = foo
>>> from foo import Foo
>>> f = Foo()
>>> dir(f)
['__doc__', '__module__']
>>> type(f)
<type 'instance'>
>>> type(Foo)
<type 'classobj'>
  • How to add a function into an object?

From here.

Add a bound method into class level:

1
2
3
4
5
6
7
8
9
10
11
>>> class A:
... def bar(self):
... print "bar"
>>> def fooFighters(self):
... print "fooFighters"
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters

Add a bound method into instance level:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> class A:
... def bar(self):
... print "bar"
...
>>> a_1 = A()
>>> a = A()
>>> def barFighters(self):
... print "barFighters"
...
>>> import types
>>> a.barFighters = types.MethodType(barFighters, a)
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters

Add a simple function to instance level:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> class A:
... pass
...
>>> dir(A)
['__doc__', '__module__']
>>> a = A()
>>> dir(a)
['__doc__', '__module__']
>>> a.__dict__
{}
>>> a.__dict__.update({'a': 1, 'b':2, 'x': lambda x: x + 1})
>>> dir(a)
['__doc__', '__module__', 'a', 'b', 'x']
>>> dir(A)
['__doc__', '__module__']
>>> a.a
1
>>> a.b
2
>>> a.x
<function <lambda> at 0x7f6770923668>
>>> a.x(1)
2
>>>
  • How to respond to non-existing artribute/method call?

From here and here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> class Dummy(object):
... def __init__(self, params):
... self.params = params
...
... def __getattr__(self, name):
... if type(self.params) is dict:
... return self.params.get(name)
... else:
... raise ValueError('params is not dict instance.')
>>> d = Dummy({'a': 1, 'b': 2, 'add': lambda x,y: x+y})
>>> d.a
1
>>> d.b
2
>>> d.add(1, 2)
3

Python Multiple Inheritance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Foo(object):
def hi(self):
print("Hello!")

Foo().hi()

class Bar(object):
def hi(self):
print("你好!")

Bar().hi()

class Wow(Foo, Bar):
pass

Wow().hi()

class Wow(Bar, Foo):
pass

Wow().hi()

Comments