How to pass function with super() when creating class dynamically? - python

Let's say I have this code:
class StaticParent:
def _print(self):
print("I'm StaticParent")
class StaticChild(StaticParent):
def _print(self):
print('StaticChild saying: ')
super()._print()
def _parent_print_proto(self):
print("I'm DynamicParent")
def _child_print_proto(self):
print('DynamicChild saying: ')
super()._print()
DynamicParent = type('DynamicParent', tuple([]), {"_print": _parent_print_proto})
DynamicChild = type('DynamicChild', (DynamicParent,), {"_print": _child_print_proto})
sc = StaticChild()
sc._print()
dc = DynamicChild()
dc._print()
And it's output is:
StaticChild saying:
I'm StaticParent
DynamicChild saying:
Traceback (most recent call last):
File "/tmp/example.py", line 28, in <module>
dc._print()
File "/tmp/example.py", line 17, in _child_print_proto
super()._print()
RuntimeError: super(): __class__ cell not found
So question is how to create prototype method for many classes that calling super()?
PS I tried to implement method with lambda but it is also not working:
DynamicChildWithLambda = type('DynamicChild', (DynamicParent,), {"_print": lambda self : print('Lambda saying: ', super()._print())})
Traceback (most recent call last):
File "/tmp/example.py", line 30, in <module>
dcwl._print()
File "/tmp/example.py", line 23, in <lambda>
DynamicChildWithLambda = type('DynamicChild', (DynamicParent,), {"_print": lambda self : print('Lambda saying: ', super()._print())})
RuntimeError: super(): __class__ cell not found
PS2 Also I tried this way:
class StaticParent:
def _print(self):
print("I'm StaticParent")
def _child_print_proto(self):
print('DynamicChild saying: ')
super(StaticParent, self)._print()
DynamicChild = type('DynamicChild', (StaticParent,), {"_print": _child_print_proto})
dc = DynamicChild()
dc._print()
DynamicChild saying:
Traceback (most recent call last):
File "/tmp/example.py", line 13, in <module>
dc._print()
File "/tmp/example.py", line 8, in _child_print_proto
super(StaticParent, self)._print()
AttributeError: 'super' object has no attribute '_print'

Methods defined within a class get a fake closure scope that automatically provides the class it was defined in to no-arg super(). When defined outside a class, it can't do this (because clearly no class is being defined at the time you define the method). But you can still make a closure the old-fashioned way, by actually writing a closure function that you manually define __class__ appropriately in:
class StaticParent:
def _print(self):
print("I'm StaticParent")
class StaticChild(StaticParent):
def _print(self):
print('StaticChild saying: ')
super()._print()
def _parent_print_proto(self):
print("I'm DynamicParent")
# Nesting allows us to make the inner function have an appropriate __class__
# defined for use by no-arg super
def _make_child_print_proto(cls):
__class__ = cls
def _child_print_proto(self):
print('DynamicChild saying: ')
super()._print()
return _child_print_proto
DynamicParent = type('DynamicParent', tuple([]), {"_print": _parent_print_proto})
DynamicChild = type('DynamicChild', (DynamicParent,), {})
# Need DynamicChild to exist to use it as __class__, bind after creation
DynamicChild._print = _make_child_print_proto(DynamicChild)
sc = StaticChild()
sc._print()
dc = DynamicChild()
dc._print()
Try it online!
Yes, it's hacky and awful. In real code, I'd just use the less common explicit two-arg super:
def _child_print_proto(self):
print('DynamicChild saying: ')
super(DynamicChild, self)._print()
That's all super() does anyway; Python hides the class it was defined in in closure scope as __class__, super() pulls it and the first positional argument, assumed to be self, and implicitly does the same thing the two-arg form did explicitly.
To be clear: You cannot pass self.__class__ manually as the first argument to two-arg super(), to simulate the __class__ that is bound in closure scope. It appears to work, and it does work, until you actually make a child of the class with that method and try to call the method on it (and the whole point of super() is you might have an arbitrarily complex class hierarchy to navigate; you can't just say "Oh, but my class is special enough to never be subclassed again"). If you do something as simple as adding:
class DynamicGrandChild(DynamicChild):
pass
dgc = DynamicGrandChild()
dgc._print()
to the self.__class__-using code from Epsi95's answer, you're going to see:
StaticChild saying:
I'm StaticParent
DynamicChild saying:
I'm DynamicParent
DynamicChild saying:
DynamicChild saying:
DynamicChild saying:
DynamicChild saying:
DynamicChild saying:
DynamicChild saying:
DynamicChild saying:
DynamicChild saying:
... repeats a thousand times or so ...
DynamicChild saying:
DynamicChild saying:
Traceback (most recent call last):
File ".code.tio", line 31, in <module>
dgc._print()
File ".code.tio", line 15, in _child_print_proto
super(self.__class__, self)._print()
File ".code.tio", line 15, in _child_print_proto
super(self.__class__, self)._print()
File ".code.tio", line 15, in _child_print_proto
super(self.__class__, self)._print()
[Previous line repeated 994 more times]
File ".code.tio", line 14, in _child_print_proto
print('DynamicChild saying: ')
RecursionError: maximum recursion depth exceeded while calling a Python object
super() is used when you're designing for inheritance. super(self.__class__, self) is used only when you're sabotaging inheritance. The only safe ways to do this involve some sort of static linkage to the class (the one that the method will be attached to, not the run time type of whatever instance it is called with), and my two solutions above are the two reasonable ways to do this (there's only one other option really, which is making the closure, and still passing the class and self explicitly, e.g. instead of defining __class__, just use super(cls, self) to explicitly use a closure variable; not sufficiently distinct, and kinda the worst of both worlds).
So, I said "there's three ways", but in fact there is a slightly nicer (but also less portable, because the API for types.FunctionType has changed over time, even though closures defined normally haven't) solution, which lets you make a utility function to bind arbitrary functions to arbitrary classes, instead of requiring you to wrap each such function in a closure-maker. And that's to literally rebuild the function as a closure directly:
import types
# Define function normally, with super(). The function can't actually be used as is though
def _child_print_proto(self):
print('DynamicChild saying: ')
super()._print()
# Utility function binding arbitrary function to arbitrary class
def bind_to_class(cls, func):
# This translates as:
# Make a new function using the same code object as the passed function,
# but tell it it has one closure scoped variable named __class__, and
# provide the class as the value to associate with it.
# This requires Python 3.8 or later (code objects didn't have a replace method
# until then, so doing this would be even uglier than it already is)
return types.FunctionType(func.__code__.replace(co_freevars=('__class__',)), func.__globals__, closure=(types.CellType(cls),))
DynamicParent = type('DynamicParent', tuple([]), {"_print": _parent_print_proto})
DynamicChild = type('DynamicChild', (DynamicParent,), {})
# Rebind the function to a new function that believes it was defined in DynamicChild
DynamicChild._print = bind_to_class(DynamicChild, _child_print_proto)
Like I said, it's super-ugly and would need rewrites pre-3.8, but it does have the mild advantage of allowing you to use bind_to_class with arbitrary super() using functions to bind them to arbitrary classes. I still think manually calling two-arg super is the safe/obvious way to go, but now you've got all the options.

Related

user defined class serialization and deserialization in python

I am very new to python : I want to serialize and deserialize my custom object in python. Please guide me on the same. I have a sample class :
import pickle
import json
class MyClass():
variable = "blah"
num = 10
def function(self):
print("this is a message inside the class.")
def get_variable():
return variable
def get_num():
return num
def main():
myObj = MyClass()
with open('/opt/infi/deeMyObj.txt', 'w') as output:
pickle.dump(myObj, output,pickle.HIGHEST_PROTOCOL)
with open('/opt/infi/deeMyObj.txt', 'r') as input:
myObjread = pickle.load(input)
print myObjread.get_variable()
print myObjread.get_num()
main()
I am getting following error :
Traceback (most recent call last):
File "sample.py", line 30, in
main()
File "sample.py", line 27, in main
print myObjread.get_variable()
TypeError: get_variable() takes no arguments (1 given)
Main intention is to read the object back.
To expand on jasonharper's comment, your get_variable and get_num methods aren't referring to the class's member variables. They should take the object as their first argument, e.g.
class MyClass:
...
def get_variable(self):
return self.variable
I think your serialization code is OK, but I might be wrong.
(Aside)
This is a bit off-topic, but another thing to note: when you define variables directly within the class block, they're defined on the class, not on objects of that class. That happens to work out in this case, since Python will look for a class-level variable of the same name if it can't find one on the object. However, if you store, say, a list in one of them and start modifying it, you'd end up sharing it between objects, which is probably not what you want. Instead you want to define them on in an __init__ method:
class MyClass:
def __init__(self):
self.variable = "blah"

Name of a Python function in a stack trace

In both Python2 and Python3, in the stack trace the __name__ of a function is not used, the original name (the one that is specified after def) is used instead.
Consider the example:
import traceback
def a():
return b()
def b():
return c()
def c():
print("\n".join(line.strip() for line in traceback.format_stack()))
a.__name__ = 'A'
b.__name__ = 'B'
c.__name__ = 'C'
a();
The output is:
File "test.py", line 16, in <module>
a();
File "test.py", line 4, in a
return b()
File "test.py", line 7, in b
return c()
File "test.py", line 10, in c
print("\n".join(line.strip() for line in traceback.format_stack()))
Why so? How do I change the name that is used in the stack trace? Where is the __name__ attribute used then?
So, basically every function has three things that can be considered being name of the function:
The original name of the code block
It's stored in the f.__code__.co_name (where f is the function object). If you use def orig_name to create function, orig_name is that name. For lambas it's <lambda>.
This attribute is readonly and can't be changed. So the only way to create function with the custom name in runtime I'm aware of is exec:
exec("""def {name}():
print '{name}'
""".format(name='any')) in globals()
any() # prints 'any'
(There is also more low-level way to do this that was mentioned in a comment to the question.)
The immutability of co_name actually makes sense: with that you can be sure that the name you see in the debugger (or just stack trace) is exactly the same you see in the source code (along with the filename and line number).
The __name__ attribute of the function object
It's also aliased to func_name.
You can modify it (orig_name.__name__ = 'updated name') and you surely do on a daily basis: #functools.wraps copies the __name__ of the decorated function to the new one.
__name__ is used by tools like pydoc, that's why you need #functools.wraps: so you don't see the technical details of every decorator in your documentation. Look at the example:
from functools import wraps
def decorator1(f):
def decorated(*args, **kwargs):
print 'start1'
f(*args, **kwargs)
return decorated
def decorator2(f):
#wraps(f)
def decorated(*args, **kwargs):
print 'start2'
f(*args, **kwargs)
return decorated
#decorator1
def test1():
print 'test1'
#decorator2
def test2():
print 'test2'
Here is the pydoc output:
FUNCTIONS
decorator1(f)
decorator2(f)
test1 = decorated(*args, **kwargs)
test2(*args, **kwargs)
With wraps there is no sign of decorated in the documentation.
Name of the reference
One more thing that can be called function name (though it hardly is) is the name of a variable or an attribute where reference to that function is stored.
If you create function with def name, the name attribute will be added to the current scope. In case of lambda you should assign the result to some variable: name = lambda: None.
Obviously you can create more than one reference to the same function and all that references can have different names.
The only way all that three things are connected to each other is the def foo statement that creates function object with both __name__ and __code__.co_name equal to foo and assign it to the foo attribute of the current scope. But they are not bound in any way and can be different from each other:
import traceback
def make_function():
def orig_name():
"""Docstring here
"""
traceback.print_stack()
return orig_name
globals()['name_in_module'] = make_function()
name_in_module.__name__ = 'updated name'
name_in_module()
Output:
File "my.py", line 13, in <module>
name_in_module()
File "my.py", line 7, in orig_name
traceback.print_stack()
Pydoc:
FUNCTIONS
make_function()
name_in_module = updated name()
Docstring here
I thank other people for comments and answers, they helped me to organize my thoughts and knowledge.
Tried to explore the CPython implementation, definitely not an expert. As pointed out in the comments, when the stack entry of f is printed, the attribute f.__code__.co_name is used. Also, f.__name__ is initially set to f.__code__.co_name, but when you modify the former, the latter is not modified accordingly.
Therefore, I tried to modify that directly, but it is not possible:
>>> f.__code__.co_name = 'g'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: readonly attribute
>>>
Why are there two ways to say a function's name? Well, according to the documentation, __name__ is defined for "class, function, method, descriptor, or generator instance", so in the case of functions it maps to that attribute, for other objects it will map to something else.

How can I get child classes to use parent variables without redefining them?

Long time reader, first time asker. Anyway, Here's the code I'm working with:
class Person(object):
def __init__(self, s):
self.name = s
self.secret = 'I HAVE THE COOKIES'
#classmethod
def shout(self):
print self.name.upper()
class Kid(Person):
def __init__(self, s):
super(Kid,self).__init__(s)
self.age = 12
b = Person('Bob')
k = Kid('Bobby')
print b.name
print k.name
print k.age
print k.secret
k.shout()
Which results in this output and error:
Bob
Bobby
12
I HAVE THE COOKIES
Traceback (most recent call last):
File "a.py", line 22, in <module>
k.shout()
File "a.py", line 8, in shout
print self.name.upper()
AttributeError: type object 'Kid' has no attribute 'name'
I assumed that Kid would be able to use the Person's shout method substituting its (the kid's) "self" for parent (where the method lives). Apparently, that's not the case. I know I could declare name outside of init, but that's both unable to accomodate inputted data and a no-no. Another alternative would be to redefine shout for every child of Person, but that's a lot of repeated code that I'm trying to avoid.
Thanks very much in advance!
The issue is that #classmethod is a method on a class. It does not have access to an instance's attributes. Specifically the method is actually passed the class object, thus self is misnamed. You should really call shout's argument cls. If you remove the #classmethod then this would all make sense and your code would work as expected.
As it is, you can think of k.shout() as equivalent to Kid.shout().

Why am I unable to perform a simple operation in the __init__ of my class?

Given below is a snippet from a class of which I am trying to create objects and getting error:
class FoF(object):
def __init__(self,path):
filepath=[]
filepath.append(self.FileOrFolder(path))
Upon executing which I get the following error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "PathOps.py", line 6, in __init__
def __init__(self,path):
NameError: global name 'filepath' is not defined
After which I tried:
filepath=[]
class FoF(object):
def __init__(self,path):
global filepath.append(self.FileOrFolder(path))
And again:
File "<stdin>", line 1, in <module>
File "PathOps.py", line 6, in __init__
global filepath.append(self.FileOrFolder(path))
NameError: global name 'filepath' is not defined
What is causing the error and how do I fix it?
Try using insted of global the special word self.
So something like this
class FoF(object):
def __init__(self,path):
self.filepath=[]
self.filepath.append(self.FileOrFolder(path))
The reason this error comes up is because what python thinks you're trying to do is one of two things:
Either you're trying to reference a global variable called filepath -- which is clear that's not what you're trying
What's not so clear is that you could also define a class attribute called filepath -- the only problem with that is that you can't define a class attribute with a function of that class. You can only do so within the class -- outside a class function
So in order to declare variables within a function you have to use the word self before it.
Edit** if you want it to be an attribute of the class -- as I'm assuming is what you meant you could do so like this:
class FoF(object):
filepath=[]
def __init__(self,path):
self.filepath.append(self.FileOrFolder(path))
I don't think you're giving us enough information. For example:
>>> class FoF(object):
... def __init__(self, path):
... junk = []
... junk.append(path)
...
>>> foo = FoF('bar/path')
produces no error.
What, exactly, are you trying to do?

Using methods of a class from another in python

I'm working through 'Dive Into Python' on Google App Engine and came across this error while attempting to call one class's methods from another:
ERROR __init__.py:463] create() takes exactly 1 argument (2 given)
Traceback (most recent call last):
File "main.py", line 35, in get
dal.create("sample-data");
File "dataAccess/dal.py", line 27, in create
self.data_store.create(data_dictionary);
TypeError: create() takes exactly 1 argument (2 given)
Here's my main class:
# filename: main.py
from dataAccess.dal import DataAccess
class MySampleRequestHandler(webapp.RequestHandler):
"""Configured to be invoked for a specific GET request"""
def get(self):
dal = DataAccess();
dal.create("sample-data"); # problem area
MySampleRequestHandler.get() tries to instantiate and invoke DataAccess which is defined else where:
# filename: dal.py
from dataAccess.datastore import StandardDataStore
class DataAccess:
"""Class responsible for wrapping the specific data store"""
def __init__(self):
self.data_store = None;
data_store_setting = config.SETTINGS['data_store_name'];
if data_store_setting == DataStoreTypes.SOME_CONFIG:
self.data_store = StandardDataStore();
logging.info("DataAccess init completed.");
def create(self, data_dictionary):
# Trying to access the data_store attribute declared in __init__
data_store.create(data_dictionary);
I thought I could call DataAccess.create() with 1 parameter for its argument, especially according to how Dive into Python notes about class method calls:
When defining your class methods, you must explicitly list self as the first
argument for each method, including __init__. When you call a method of an
ancestor class from within your class, you must include the self argument.
But when you call your class method from outside, you do not specify anything
for the self argument; you skip it entirely, and Python automatically adds the
instance reference for you.
In self.data_store.create(data_dictionary), the self.data_store refers to the object created by self.data_store = StandardDataStore() in the __init__ method.
It looks like the create method of a StandardDataStore object doesn't expect an additional argument.
It should be self.data_store.create(data_dictionary);

Categories