Python types.MethodType() - python

Why do I have to assign the result of types.MethodType() to an attribute before I use it via p.** in my example below. As I think, the method types.MethodType() has already bind the method onto the instance. Hope someone can help. Thanks a lot.
Here is my code:
import types
class Person(object):
def __init__(self, newName):
self.name = newName
def eat(self, food):
print("%s is eating %s" % (self.name, food))
def getName(self):
print("My name is %s" % self.name)
def main():
p = Person("Peter")
p.eat("Pork bean")
types.MethodType(getName, p) #p.getName = types.MethodType(getName, p)
p.getName()
if __name__ == "__main__":
main()
[]
[]

The following works to graft a method onto a class:
class A(object):
def __init__(self, value):
self.value = value
def my_method(self): # `self` is a plain name, not a reserved word or something.
return 'Value via a grafted method is %s' % self.value
A.grafted = my_method
assert A('here').grafted() == 'Value via a grafted method is here'
Yes, a plain assignment works.
Grafting a method onto an instance is harder but doable:
a = A('okay')
a.instance_patched = types.MethodType(my_method, a)
assert a.instance_patched() == 'Value via a grafted method is okay'
Here types.MethodType(my_method, a) creates a method bound to a.
Doing the following is elucidating:
print(my_method)
print(A.grafted)
print(a.instance_patched)
print(a.instance_patched.__func__) # in python 2.x, .im_func

Make sure that getName() is properly indented, on the same level as eat().
There is no need for types.MethodType() for you case:
class Person(object):
def __init__(self, newName):
self.name = newName
def eat(self, food):
print("%s is eating %s" % (self.name, food))
def getName(self):
print("My name is %s" % self.name)
def main():
p = Person("Peter")
p.eat("Pork bean")
p.getName()
if __name__ == "__main__":
main()
Output:
Peter is eating Pork bean
My name is Peter
Furthermore:
types.MethodType(getName, p)
does do anything useful, because you throw away the return value.

Because types.MethodType doesn't mutate either the function or the instance, it just returns a new callable object which holds both the function and the instance.
(You seem to have been expecting "bind" to mean "mutate one of the objects to hold a reference to the other", and so it sorta makes sense that you thought of it as adding the function onto the instance, but the best way to think of "bind" is that it produces a new function copy which has the instance bound as its first argument.)

Related

How to not run init based on __new__ method

I have the following class, which acts as a collection of people:
class Person:
PERSONS = dict() # name ==> instance
def __new__(cls, *args, **kwargs):
name = kwargs.get('name') or '' if not args else args[0]
print ('Name: %s' % name)
if name in cls.PERSONS:
print ('Returning found person!')
return cls.PERSONS[name]
else:
print ('Initializing new person')
return super(Person, cls).__new__(cls)
def __init__(self, name):
print ("Running init")
self.name = name
Person.PERSONS[name] = self
If a person is found, it returns that person, otherwise it creates a new one. And when I run it it works:
>>> p1 = Person('Julia')
Name: Julia
Initializing new person
Running init
>>> p2 = Person('Julia')
Name: Julia
Returning found person!
Running init # <== how to get this not to run?
>>> p1 is p2
True
However, if the person is found, I don't want the __init__ method to run. How would I "skip" the init method based on the return of the __new__ ?
One option is to add a conditional in the __init__, such as:
def __init__(self, name):
if name in Person.PERSONS: return # don't double-initialize
print ("Running init")
self.name = name
Person.PERSONS[name] = self
But I was hoping there might be a cleaner approach.
#MadPhysicist's idea of using a metaclass with a custom __call__ method is correct but the implementation included in the answer is quite off. Instead, the custom __call__ method should use the name of the person, rather than a new Person object, to check if a given name has an existing entry in the PERSONS dict:
class PersonMeta(type):
def __call__(cls, name):
print ('Name: %s' % name)
if name in cls.PERSONS:
print ('Returning found person!')
return cls.PERSONS[name]
print('Initializing new person')
obj = cls.__new__(cls, name)
cls.__init__(obj, name)
cls.PERSONS[name] = obj
return obj
class Person(metaclass=PersonMeta):
PERSONS = dict() # name ==> instance
def __init__(self, name):
print ("Running init")
self.name=name
p1=Person('Julia')
p2=Person('Julia')
print(p1 is p2)
This outputs:
Name: Julia
Initializing new person
Running init
Name: Julia
Returning found person!
True
Instead of trying to skip __init__, put your initialization in __new__. In general, most classes should only implement one of __new__ and __init__, or things get messy.
Also, trying to have a class act as a collection of anything is usually a bad idea. Instead of trying to make your class itself manage its instances, it tends to be a better idea to give that role to a dedicated collection object. This makes it easier to manage object lifetimes, have multiple containers, avoid weird __new__ problems, etc.
The problem I find in your approach is that the __new__ dunder method is triggered just before the __init__. Once said that, it's not that easy to change that behavior.
Instead of handling the new Person's creation inside __new__, create a class method (e.g. create_person) and update the PERSONS dict if needed.
class Person:
def __init__(self, name):
print("Running init\n")
self.name = name
class PersonFactory:
PERSONS = dict()
#classmethod
def create_person(cls, name):
print('Name: %s' % name)
if name in cls.PERSONS:
print ('Returning found person!')
return cls.PERSONS[name]
print('Initializing new person')
cls.PERSONS[name] = Person(name)
return cls.PERSONS[name]
if __name__ == '__main__':
PersonFactory.create_person('Julia')
PersonFactory.create_person('Julia')

Creating methods on the fly for a class instance

The following program is unable to create a function of a class
class MyClass(object):
def __init__(self, name=""):
self.name = name
def read_name(self):
return self.name
# First argument should be a ref to class
def callback(fcn, arg):
fcn.name=arg
# Create a instance of class
a = MyClass("Blue")
# Lets add new member functions
setattr(a, 'callback1', callback)
setattr(a, 'callback2', callback)
print a.read_name()
print a.callback1("purple") #! FAILS
print a.callback2("cyan") #! FAILS
What is the right way of creating a class member function automatically?
I want to create 'N' callback functions, they all will modify some common/uncommon class data (A shared dict)
EDIT 1
I wish to collect information from 'N' separate/parallel threads by passing callback functions. I do not know beforehand how many callback functions I need thus I want to create them on fly.
EDIT 2
I have a dictionary(d) where I am storing the information of different processes. The dictionary(d) is accessed within the callback. But because the same callback function is called at different threads, the dictionary data gets garbled. As a quickfix, I thought of creating separate callbacks.
If you know what you're doing, you'd want to try
import types
setattr(a, 'callback1', types.MethodType(callback, a, MyClass))
In short: when grafting a method, assign it to the class, not to the instance.
Here's an elucidating example.
class A(object):
"""As trivial as a class can get."""
def foo(self):
return self.bar(1) + self.baz()
# Rework everything!
def new_bar(self, x):
return 'I got %r' % x
def new_baz(self):
return ' and I\'m okay!'
A.bar = new_bar
A.baz = new_baz
print A().foo()
Now grafting method to an instance.
a = A()
# An instance attribute is a bound method;
# when we replace it with a function, we lose access to self.
a.bar = lambda x: x * 100
A.baz = lambda self: 42
assert a.foo() == 142
# We can do better, though.
from types import MethodType
a2 = A()
a2.foo = MethodType(lambda self: 'I know myself, my class is %s' % self.__class__.__name__, a2)
print a2.foo()
Note how you don't need setattr to set an attribute, even an unknown attribute. You may remember that you don't use setattr in __init__ either.
You can't add a class method to an instance; you have to add it to the class:
setattr(MyClass, 'callback1', callback)
But it's still a terrible idea. Why would you want this functionality?
Edit: keep your callbacks in a container instead:
class MyClass(object):
def __init__(self, name=""):
self.name = name
self.callbacks = []
def callback(self, idx, arg):
self.callbacks[idx](self, arg)
# First argument should be a ref to class
def callback(fcn, arg):
fcn.name=arg
# Create a instance of class
a = MyClass("Blue")
# Lets add new member functions
a.callbacks.append(callback)
a.callbacks.append(callback)
print a.name
a.callback(0, "purple")
print a.name
a.callback(1, "cyan")
print a.name

Procedurally created decorator functions

My goal is to create a function that will procedurally generate a series of other functions within a class from serialized data.
This is easy enough using dict , but...
i would like for each function to be initialized with the #property decorator (or a similar custom decorator) so that i can call these functions like attributes
Basically, I would like to do something like the following:
class myClass(object):
def __init__(self):
self.makeFuncs(['edgar','allan','poe'])
def makeFuncs(self, data):
for item in data:
self.__dict__[item] = '[%s] <--- is_the_data' % item
myInstance = myClass()
print myInstance.poe
#'[poe] <--- is_the_data'
Got any Ideas?
You can dynamically add propertys, but properties are added to the class object, not the instance.
Here's an example:
def make_prop(cls, p):
def f(self):
print 'IN %s' % p
return '[%s]' % p
return f
class myClass(object):
pass
# add the properties
for p in ('edgar','allan','poe'):
setattr(myClass, p, property(make_prop(myClass, p)))
y = myClass()
print y.a
print y.b
Prints:
IN allan
[allan]
IN poe
[poe]
Also, it is essential to use make_prop to create the function object, instead of creating them directly inside the for loop, due to python's lexical scoping. I.e. this won't work as expected:
# add the properties
for p in ('edgar','allan','poe'):
def f(self):
print 'IN %s' % p
return '[%s]' % p
setattr(myClass, p, property(f))
Here is the answer I came to for procedurally adding properties to a custom shader class in maya.
Thx #shx2 !
import maya.cmds as mc
import sushi.maya.node.dependNode as dep
class Shader(dep.DependNode):
def __init__(self, *args, **kwargs):
super(Shader, self).__init__(*args, **kwargs)
makeProps(self.__class__, ['color','transparency','ambientColor','incandescence','diffuse','translucence','translucenceDepth','translucenceFocus'])
def createShaderProperties(attrName):
def getterProp(self):
return mc.getAttr('%s.%s' % (self.name, attrName))[0]
def setterProp(self, value):
mc.setAttr('%s.%s' % (self.name, attrName), *value, type = 'double3')
return (getterProp, setterProp)
def makeProps(cls, data):
for dat in data:
getterProp, setterProp = createShaderProperties(dat)
setattr(cls, dat, property(getterProp))
setattr(cls, dat, property.setter(cls.__dict__[dat],setterProp))
Your current idea won't work because property objects need to be in the class in order to work (they are descriptors). Since your list of functions is specific to each instance, that won't be possible.
However, you can make the general idea work using __getattr__. Here's an implementation that I think does what you want given a dictionary mapping from names to functions:
class MyClass(object):
def __init__(self, func_dict):
self.func_dict = func_dict
def __getattr__(self, name):
if name in self.func_dict:
return self.func_dict[name]() # call the function
raise AttributeError("{} object has no attribute named {}"
.format(self.__class__.__name__, name)) # or raise an error

Dynamically assigning function implementation in Python

I want to assign a function implementation dynamically.
Let's start with the following:
class Doer(object):
def __init__(self):
self.name = "Bob"
def doSomething(self):
print "%s got it done" % self.name
def doItBetter(self):
print "Done better"
In other languages we would make doItBetter an anonymous function and assign it to the object. But no support for anonymous functions in Python. Instead, we'll try making a callable class instance, and assign that to the class:
class Doer(object):
def __init__(self):
self.name = "Bob"
class DoItBetter(object):
def __call__(self):
print "%s got it done better" % self.name
Doer.doSomething = DoItBetter()
doer = Doer()
doer.doSomething()
That gives me this:
Traceback (most recent call last): Line 13, in
doer.doSomething() Line 9, in call
print "%s got it done better" % self.name AttributeError: 'DoItBetter' object has no attribute 'name'
Finally, I tried assigning the callable to the object instance as an attribute and calling it:
class Doer(object):
def __init__(self):
self.name = "Bob"
class DoItBetter(object):
def __call__(self):
print "%s got it done better" % self.name
doer = Doer()
doer.doSomething = DoItBetter()
doer.doSomething()
This DOES work as long as I don't reference self in DoItBetter, but when I do it gives me an name error on self.name because it's referencing the callable's self, not the owning class self.
So I'm looking for a pythonic way to assign an anonymous function to a class function or instance method, where the method call can reference the object's self.
Your first approach was OK, you just have to assign the function to the class:
class Doer(object):
def __init__(self):
self.name = "Bob"
def doSomething(self):
print "%s got it done" % self.name
def doItBetter(self):
print "%s got it done better" % self.name
Doer.doSomething = doItBetter
Anonymous functions have nothing to do with this (by the way, Python supports simple anonymous functions consisting of single expressions, see lambda).
yak's answer works great if you want to change something for every instance of a class.
If you want to change the method only for a particular instance of the object, and not for the entire class, you'd need to use the MethodType type constructor to create a bound method:
from types import MethodType
doer.doSomething = MethodType(doItBetter, doer)

Python Object Oriented Programming

Am trying to understand object oriented programming with python. Am new to programming.
I have this class that is giving me an error I don't understand and I will be glad if anyone can throw more light on this for me:
class TimeIt(object):
def __init__(self, name):
self.name = name
def test_one(self):
print 'executed'
def test_two(self, word):
self.word = word
i = getattr(self, 'test_one')
for i in xrange(12):
sleep(1)
print 'hello, %s and %s:' % (self.word, self.name),
i()
j = TimeIt('john')
j.test_two('mike')
If I run this class I get 'int' object is not callable" TypeError
However, if I precede the i with self (self.i), it works.
class TimeIt(object):
def __init__(self, name):
self.name = name
def test_one(self):
print 'executed'
def test_two(self, word):
self.word = word
self.i = getattr(self, 'test_one')
for i in xrange(12):
sleep(1)
print 'hello, %s and %s:' % (self.word, self.name),
self.i()
My question is, doesn't i = getattr(self, 'test_one') assign the test_one function to i?
How come i() doesn't work?
Why does self.i() work?
Why is i an int (hence the 'int' object is not callable TypeError)?
That's a lot of questions. Thanks in advance
You're overwriting i within loop. When you're "preceding" i with self, you're creating different variable, which is not overwritten.
#SilentGhost is right on the money with his answer.
To illustrate, try chaning the test_two method to this:
def test_two(self, word):
self.word = word
i = getattr(self, 'test_one')
for some_other_variable_besides_i in xrange(12):
sleep(1)
print 'hello, %s and %s:' % (self.word, self.name),
i()
Your code overwrite the variable i (set as a method) within the for loop (see comments)
def test_two(self, word):
self.word = word
i = getattr(self, 'test_one')
# i is now pointing to the method self.test_one
for i in xrange(12):
# now i is an int based on it being the variable name chosen for the loop on xrange
sleep(1)
print 'hello, %s and %s:' % (self.word, self.name),
i()
In addition, you certainly don't need to assign the test_one method to a variable like i. Instead, you can just call the method replacing
i()
with
self.test_one()

Categories