unbound method <method> must be called with <class> instance as first argument - python-2.7

I would like to provide default behaviour for a class as illustrated below.
import numpy as np
class Test:
def __init__(self, my_method=None):
self.my_method = my_method or np.min
Test().my_method([1, 2, 3]) # >>> 1
The code works as expected. To keep all the default values together for easier code maintenance I wanted to change the code to
import numpy as np
class Test:
default_method = np.min
def __init__(self, my_method=None):
self.my_method = my_method or Test.default_method
Test().my_method([1, 2, 3]) # >>> TypeError
but the call to my_method fails with the error message unbound method amin() must be called with Test instance as first argument (got list instance instead). Oddly, the code works as expected if I use the builtin min rather than np.min, i.e. the following works as expected.
import numpy as np
class Test:
default_method = min # no np.
def __init__(self, my_method=None):
self.my_method = my_method or Test.default_method
Test().my_method([1, 2, 3]) # >>> 1
What am I missing?

Any function stored as an attribute on a class object is treated as a method by Python. On Python 2, that means it requires the first argument to be an instance of the class (which will be passed automatically if the attribute is requested via an instance). On Python 3, unbound methods no longer check their arguments in that way (so your code would work as written).
To work around the issue on Python 2, try wrapping the default_method value with staticmethod:
class Test(object):
default_method = staticmethod(np.min)
#...
This might not be a bad idea even on Python 3, since you'll also be able to use self.default_method rather than explicitly naming the class.
As for why the code worked with min but not np.min, that's because they are implemented differently. You can see that from their types:
>>> type(min)
<class 'builtin_function_or_method'>
>>> type(np.min)
<class 'function'>
Regular functions (like np.min) act as descriptors when they're attributes of a class (thus getting the "binding" behavior that was causing your issue). Builtin functions like min don't support the descriptor protocol, so the issue doesn't come up.

Related

how to fit a method belonging to an instance with pymc3?

I failed to fit a method belonging to an instance of a class, as a Deterministic function, with PyMc3. Can you show me how to do that ?
For simplicity, my case is summarised below with a simple example. In reality, my constraint is that everything is made through a GUI and actions like ‘find_MAP’ should be inside methods linked to pyqt buttons.
I want to fit the function ‘FunctionIWantToFit’ over the data points. Problem, the following code:
import numpy as np
import pymc3 as pm3
from scipy.interpolate import interp1d
import theano.tensor as tt
import theano.compile
class cprofile:
def __init__(self):
self.observed_x = np.array([0.3,1.4,3.1,5,6.8,9,13.4,17.1])
self.observations = np.array([6.25,2.75,1.25,1.25,1.5,1.75,1.5,1])
self.x = np.arange(0,18,0.5)
#theano.compile.ops.as_op(itypes=[tt.dscalar,tt.dscalar,tt.dscalar],
otypes=[tt.dvector])
def FunctionIWantToFit(self,t,y,z):
# can be complicated but simple in this example
# among other things, this FunctionIWantToFit depends on a bunch of
# variables and methods that belong to this instance of the class cprofile,
# so it cannot simply be put outside the class ! (like in the following example)
val=t+y*self.x+z*self.x**2
interp_values = interp1d(self.x,val)
return interp_values(self.observed_x)
def doMAP(self):
model = pm3.Model()
with model:
t = pm3.Uniform("t",0,5)
y = pm3.Uniform("y",0,5)
z = pm3.Uniform("z",0,5)
MyModel = pm3.Deterministic('MyModel',self.FunctionIWantToFit(t,y,z))
obs = pm3.Normal('obs',mu=MyModel,sd=0.1,observed=self.observations)
start = pm3.find_MAP()
print('start: ',start)
test=cprofile()
test.doMAP()
gives the following error:
Traceback (most recent call last):
File "<ipython-input-15-3dfb7aa09f84>", line 1, in <module>
runfile('/Users/steph/work/profiles/GUI/pymc3/so.py', wdir='/Users/steph/work/profiles/GUI/pymc3')
File "/Users/steph/anaconda/lib/python3.5/site-packages/spyder/utils/site/sitecustomize.py", line 866, in runfile
execfile(filename, namespace)
File "/Users/steph/anaconda/lib/python3.5/site-packages/spyder/utils/site/sitecustomize.py", line 102, in execfile
exec(compile(f.read(), filename, 'exec'), namespace)
File "/Users/steph/work/profiles/GUI/pymc3/so.py", line 44, in <module>
test.doMAP()
File "/Users/steph/work/profiles/GUI/pymc3/so.py", line 38, in doMAP
MyModel = pm3.Deterministic('MyModel',self.FunctionIWantToFit(x,y,z))
File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gof/op.py", line 668, in __call__
required = thunk()
File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gof/op.py", line 912, in rval
r = p(n, [x[0] for x in i], o)
File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/compile/ops.py", line 522, in perform
outs = self.__fn(*inputs)
TypeError: FunctionIWantToFit() missing 1 required positional argument: 'z'
What’s wrong ?
remark 1: I systematically get an error message concerning the last parameter of ‘FunctionIWantToFit’. here it’s ‘z’ but if I remove z from the signature, the error message concerns ‘y’ (identical except from the name of the variable). if I add a 4th variable ‘w’ in the signature, the error message concerns ‘w’ (identical except from the name of the variable).
rk2: it looks like I missed something very basic in ‘theano’ or ‘pymc3’, because when I put ‘FunctionIWantToFit’ outside the class, it works. See the following example.
class cprofile:
def __init__(self):
self.observations = np.array([6.25,2.75,1.25,1.25,1.5,1.75,1.5,1])
def doMAP(self):
model = pm3.Model()
with model:
t = pm3.Uniform("t",0,5)
y = pm3.Uniform("y",0,5)
z = pm3.Uniform("z",0,5)
MyModel = pm3.Deterministic('MyModel',FunctionIWantToFit(t,y,z))
obs = pm3.Normal('obs',mu=MyModel,sd=0.1,observed=self.observations)
start = pm3.find_MAP()
print('start: ',start)
#theano.compile.ops.as_op(itypes=[tt.dscalar,tt.dscalar,tt.dscalar],
otypes=[tt.dvector])
def FunctionIWantToFit(t,y,z):
observed_x = np.array([0.3,1.4,3.1,5,6.8,9,13.4,17.1])
x = np.arange(0,18,0.5)
val=t+y*x+z*x**2
interp_values = interp1d(x,val)
return interp_values(observed_x)
test=cprofile()
test.doMAP()
gives:
Warning: gradient not available.(E.g. vars contains discrete variables). MAP estimates may not be accurate for the default parameters. Defaulting to non-gradient minimization fmin_powell.
WARNING:pymc3:Warning: gradient not available.(E.g. vars contains discrete variables). MAP estimates may not be accurate for the default parameters. Defaulting to non-gradient minimization fmin_powell.
Optimization terminated successfully.
Current function value: 1070.673818
Iterations: 4
Function evaluations: 179
start: {'t_interval_': array(-0.27924150484602733), 'y_interval_': array(-9.940000425802811), 'z_interval_': array(-12.524909223913992)}
Except that I don’t know how to do that without big modifications in several modules, since the real ‘FunctionIWantToFit’ depends on a bunch of variables and methods that belong to this instance of the class profile.
In fact I 'm not even sure I know how to do that since ‘FunctionIWantToFit’ should then have objects in arguments (that I currently use via self) and I'm not sure how to do that with the theano decorator.
So I would prefer to avoid this solution... unless necessary. then I need explanations on how to implement it...
added on april 9, 2017:
Even without the interpolation question, it doesn't work because I must have missed something obvious with theano and/or pymc3. Please can you explain the problem ? I just want to compare model and data. First, it's such a shame being stuck to pymc2. ; second, I'm sure I'm not the only one with such a basic problem.
For example, let's consider variations around this very basic code:
import numpy as np
import theano
import pymc3
theano.config.compute_test_value = 'ignore'
theano.config.on_unused_input = 'ignore'
class testclass:
x = np.arange(0,18,0.5)
observed_x = np.array([0.3,1.4,3.1,5,6.8,9,13.4,17.1])
observations = np.array([6.25,2.75,1.25,1.25,1.5,1.75,1.5,1])
def testfunc(self,t,y,z):
t2 = theano.tensor.dscalar('t2')
y2 = theano.tensor.dscalar('y2')
z2 = theano.tensor.dscalar('z2')
val = t2 + y2 * self.observed_x + z2 * self.observed_x**2
f = theano.function([t2,y2,z2],val)
return f
test=testclass()
model = pymc3.Model()
with model:
t = pymc3.Uniform("t",0,5)
y = pymc3.Uniform("y",0,5)
z = pymc3.Uniform("z",0,5)
with model:
MyModel = pymc3.Deterministic('MyModel',test.testfunc(t,y,z))
with model:
obs = pymc3.Normal('obs',mu=MyModel,sd=0.1,observed=test.observations)
this code fails at the last line with the error message: TypeError: unsupported operand type(s) for -: 'TensorConstant' and 'Function'
if I change 'testfunc' into:
def testfunc(self,t,y,z):
t2 = theano.tensor.dscalar('t2')
y2 = theano.tensor.dscalar('y2')
z2 = theano.tensor.dscalar('z2')
val = t2 + y2 * self.observed_x + z2 * self.observed_x**2
f = theano.function([t2,y2,z2],val)
fval = f(t,y,z,self.observed_x)
return fval
the code fails at the 'MyModel =' line with error TypeError: ('Bad input argument to theano function with name "/Users/steph/work/profiles/GUI/pymc3/theanotest170409.py:32" at index 0(0-based)', 'Expected an array-like object, but found a Variable: maybe you are trying to call a function on a (possibly shared) variable instead of a numeric array?')
if I go back to the original 'testfunc' but change the last 'with model' lines with:
with model:
fval = test.testfunc(t,y,z)
obs = pymc3.Normal('obs',mu=fval,sd=0.1,observed=test.observations)
the error is the same as the first one.
I presented here only 3 tries but I would like to underline that I tried many many combinations, simpler and simpler until these ones, during hours. I have the feeling pymc3 shows a huge change of spirit, compared to pymc2, that I didn't get and is poorly documented...
Ok, let's do this by parts. First I'll explain the error messages that you got, and then I'll tell you how I would proceed.
On the first question, the direct reason why you're getting a complaint on the missing parameters is because your function, defined inside the class, takes as input (self, t, y, z), while you're declaring it in the op decorator as having only three inputs (t, y, z). You would have to declare the inputs as being four in your decorator to account for the class instance itself.
On "added on april 9, 2017:", the first code will not work because the output of test.testfunc(t,y,z) is a theano function itself. pymc3.Deterministic is expecting it to output theano variables (or python variables). Instead, make test.testfun output val = t2 + y2 * self.observed_x + z2 * self.observed_x**2 directly.
Then, on "if I change 'testfunc' into:", you get that error because of the way pymc3 is trying to work with theano functions. Long story short, the problem is that when pymc3 is making use of this function, it will send it theano variables, while fval is expecting numerical variables (numpy arrays or other). As in the previous paragraph, you just need to output val directly: no need to compile any theano function for this.
As for how I would proceed, I would try to declare the class instance as input to the theano decorator. Unfortunately, I can't find any documentation on how to do this and it might actually be impossible (see this old post, for example).
Then I would try to pass everything the function needs as inputs and define it outside of the class. This could be quite cumbersome and if it needs methods as input, then you run into additional problems.
Another way of doing this is to create a child class of theano.gof.Op whose init method takes your class (or rather an instance of it) as input and then define your perform() method. This would look something like this:
class myOp(theano.gof.Op):
""" These are the inputs/outputs you used in your as_op
decorator.
"""
itypes=[tt.dscalar,tt.dscalar,tt.dscalar]
otypes=[tt.dvector]
def __init__(self, myclass):
""" myclass would be the class you had from before, which
you called cprofile in your first block of code."""
self.myclass = myclass
def perform(self,node, inputs, outputs):
""" Here you define your operations, but instead of
calling everyting from that class with self.methods(), you
just do self.myclass.methods().
Here, 'inputs' is a list with the three inputs you declared
so you need to unpack them. 'outputs' is something similar, so
the function doesn't actually return anything, but saves all
to outputs. 'node' is magic juice that keeps the world
spinning around; you need not do anything with it, but always
include it.
"""
t, y, z = inputs[0][0], inputs[0][1], inputs[0][2]
outputs[0][0] = t+y*self.myclass.x+z*self.myclass.x**2
myop = myOp(myclass)
Once you have done this, you can use myop as your Op for the rest of your code. Note that some parts are missing. You can check my example for more details.
As for the example, you do not need to define the grad() method. Because of this, you can do all operations in perform() in pure python, if that helps.
Alternatively, and I say this with a smirk on my face, if you have access to the definition of the class you're using, you can also make it inherit from theano.gof.Op, create the perform() method (as in my other example, where you left a message) and try to use it like that. It could create conflicts with whatever else you're doing with that class and it's probably quite hard to get right, but might be fun to try.
theano.compile.ops.as_op is just a short-hand for defining simple Theano Ops. If you want to code more involved ones, it is better to define it in a separate class. Objects of this class could of course take a reference to an instance of your cprofile if that really is necessary.
http://deeplearning.net/software/theano/extending/extending_theano.html
I finally converged toward the successful code below:
import numpy as np
import theano
from scipy.interpolate import interp1d
import pymc3 as pm3
theano.config.compute_test_value = 'ignore'
theano.config.on_unused_input = 'ignore'
class cprofile:
observations = np.array([6.25,2.75,1.25,1.25,1.5,1.75,1.5,1])
x = np.arange(0,18,0.5)
observed_x = np.array([0.3,1.4,3.1,5,6.8,9,13.4,17.1])
def doMAP(self):
model = pm3.Model()
with model:
t = pm3.Uniform("t",0,5)
y = pm3.Uniform("y",0,5)
z = pm3.Uniform("z",0,5)
obs=pm3.Normal('obs',
mu=FunctionIWantToFit(self)(t,y,z),
sd=0.1,observed=self.observations)
start = pm3.find_MAP()
print('start: ',start)
class FunctionIWantToFit(theano.gof.Op):
itypes=[theano.tensor.dscalar,
theano.tensor.dscalar,
theano.tensor.dscalar]
otypes=[theano.tensor.dvector]
def __init__(self, cp):
self.cp = cp # note cp is an instance of the 'cprofile' class
def perform(self,node, inputs, outputs):
t, y, z = inputs[0], inputs[1], inputs[2]
xxx = self.cp.x
temp = t+y*xxx+z*xxx**2
interpolated_concentration = interp1d(xxx,temp)
outputs[0][0] = interpolated_concentration(self.cp.observed_x)
testcp=cprofile()
testcp.doMAP()
thanks to the answer by Dario because I was too slow to understand the first answer by myself. I get it retrospectively but I strongly think the pymc3 doc is painfully unclear. It should contain very simple and illustrative examples.
However I didn’t succed in doing anything that work following the comment by Chris. Could anyone explain and/or give an example ?
One more thing: I don’t know whether my example above is efficient or could be simplified. In particular it gives me the impression the instance ‘testcp’ is copied twice in memory. More comments/answers are welcome to go further.

Selenium Webdriver Python page object MainPageLocators class why does it use an asterisk infront of the class name

I have been following the page object model for my Selenium Webdriver tests in Python. I have followed the sample from GitHub.
URL is: https://github.com/baijum/selenium-python/blob/master/source/page-objects.rst
When you call the locator from the MainPageLocators class e.g from the URL.
element = self.driver.find_element(*MainPageLocators.GO_BUTTON)
It uses an asterisk in front of the class name *MainPageLocators.
Why does it use * ?
It does not work if you use MainPageLocators, you have to use *MainPageLocators.
This is no good because when i use the WebDriverWait it does not work with *MainPageLocators or MainPageLocators.
E.g.
element = WebDriverWait(self.driver, 20).until(EC.element_to_be_clickable((*MainPageLocators.locator)))
I have to to do it this way for it to work which defeats the purpose of having the locators in one place.
element = WebDriverWait(self.driver, 20).until(EC.element_to_be_clickable((By.ID, 'button_id')))
Why the asterisk in front of MainPageLocators?
Why does *MainPageLocators not work inside the WebDriverWait?
It does work if you do
self.driver.find_element(*MainPageLocators.locator)
But it does not work if you use it in the WebDriverWait
Thanks,
Riaz
In this context, * is the argument-unpacking operator. It tells Python to unpack the values in the sequence that follows and pass them as arguments to the function. For example,
foo(*(1, 2, 3))
is equivalent to
foo(1, 2, 3)
Since MainPageLocators.GO_BUTTON is defined like this:
class MainPageLocators(object):
"""A class for main page locators. All main page locators should come here"""
GO_BUTTON = (By.ID, 'submit')
it follows that find_element(*MainPageLocators.GO_BUTTON) is equivalent to
find_element(By.ID, 'submit')
This works since find_element expects 2 arguments.
In contrast, EC.element_to_be_clickable expects a single 2-tuple as its argument. Therefore you would not want to use the argument-unpacking operator here. Instead, just pass the 2-tuple directly:
wait = WebDriverWait(self.driver, 20)
element = wait.until(EC.element_to_be_clickable((By.ID, 'submit'))
or use
element = wait.until(EC.element_to_be_clickable(MainPageLocators.GO_BUTTON)

IronPython strange method calling

Why does calling a method like this in ironPython work?:
from System.Collections.Generic import List
class test:
mem = None
def __init__(self):
# !No Instance created !!!
self.mem = List[int]
def doSomeThing(self):
if self.mem.Contains((List[int](), 123):
pass
I can't get the behaviour of IronPython in this case: self.mem.Contains((List[int](), 123):. Does any one has an explanation for this?
EDIT
Is self.mem only the type, and Contains will always return False? If this is true, it seems to be a nice feature :)
Thank you!
This is true of normal Python classes as well:
class Foo(object):
def bar(self):
pass
f = Foo
f.bar(Foo())
It's the difference between bound (Foo().bar) and unbound (Foo.bar) methods. It's not so much a feature as a side effect of how methods are implemented in Python.
Contains is always false because it is working on an empty list, which contains nothing.

How to convert string to function reference in python

I have a class that transforms some values via a user-specified function. The reference to the function is passed in the constructor and saved as an attribute. I want to be able to pickle or make copies of the class. In the __getstate__() method, I convert the dictionary entry to a string to make it safe for pickling or copying. However, in the __setstate__() method I'd like to convert back from string to function reference, so the new class can transform values.
class transformer(object):
def __init__(self, values=[1], transform_fn=np.sum):
self.values = deepcopy(values)
self.transform_fn = transform_fn
def transform(self):
return self.transform_fn(self.values)
def __getstate__(self):
obj_dict = self.__dict__.copy()
# convert function reference to string
obj_dict['transform_fn'] = str(self.transform_fn)
return obj_dict
def __setstate__(self, obj_dict):
self.__dict__.update(obj_dict)
# how to convert back from string to function reference?
The function reference that is passed can be any function, so solutions involving a dictionary with a fixed set of function references is not practical/flexible enough. I would use it like the following.
from copy import deepcopy
import numpy as np
my_transformer = transformer(values=[0,1], transform_fn=np.exp)
my_transformer.transform()
This outputs: array([ 1. , 2.71828183])
new_transformer = deepcopy(my_transformer)
new_transformer.transform()
This gives me: TypeError: 'str' object is not callable, as expected.
You could use dir to access names in a given scope, and then getattr to retrieve them.
For example, if you know the function is in numpy:
import numpy
attrs = [x for x in dir(numpy) if '__' not in x] # I like to ignore private vars
if obj_dict['transform_fn'] in attrs:
fn = getattr(numpy, obj_dict['transform_fn'])
else:
print 'uhoh'
This could be extended to look in other modules / scopes.
If you want to search in the current scope, you can do the following (extended from here):
import sys
this = sys.modules[__name__]
attrs = dir(this)
if obj_dict['transform_fn'] in attrs:
fn = getattr(this, obj_dict['transform_fn'])
else:
print 'Damn, well that sucks.'
To search submodules / imported modules you could iterate over attrs based on type (potentially recursively, though note that this is an attr of this).
I think if you are asking the same question, I came here for.
The answer is simply use eval() to evaluate the name.
>>> ref = eval('name')
This is going to return what 'name' references in the scope where the eval is
executed, then you can determine if that references is a function.

Extending SWIG builtin classes

The -builtin option of SWIG has the advantage of being faster, and of being exempt of a bug with multiple inheritance.
The setback is I can't set any attribute on the generated classes or any subclass :
-I can extend a python builtin type like list, without hassle, by subclassing it :
class Thing(list):
pass
Thing.myattr = 'anything' # No problem
-However using the same approach on a SWIG builtin type, the following happens :
class Thing(SWIGBuiltinClass):
pass
Thing.myattr = 'anything'
AttributeError: type object 'Thing' has no attribute 'myattr'
How could I work around this problem ?
I found a solution quite by accident. I was experimenting with metaclasses, thinking I could manage to override the setattr and getattr functions of the builtin type in the subclass.
Doing this I discovered the builtins already have a metaclass (SwigPyObjectType), so my metaclass had to inherit it.
And that's it. This alone solved the problem. I would be glad if someone could explain why :
SwigPyObjectType = type(SWIGBuiltinClass)
class Meta(SwigPyObjectType):
pass
class Thing(SWIGBuiltinClass):
__metaclass__ = Meta
Thing.myattr = 'anything' # Works fine this time
The problem comes from how swig implemented the classes in "-builtin" to be just like builtin classes (hence the name).
builtin classes are not extensible - try to add or modify a member of "str" and python won't let you modify the attribute dictionary.
I do have a solution I've been using for several years.
I'm not sure I can recommend it because:
It's arguably evil - the moral equivalent of casting away const-ness in C/C++
It's unsupported and could break in future python releases
I haven't tried it with python3
I would be a bit uncomfortable using "black-magic" like this in production code - it could break and is certainly obscure - but at least one giant corporation IS using this in production code
But.. I love how well it works to solve some obscure features we wanted for debugging.
The original idea is not mine, I got it from:
https://gist.github.com/mahmoudimus/295200 by Mahmoud Abdelkader
The basic idea is to access the const dictionary in the swig-created type object as a non-const dictionary and add/override any desired methods.
FYI, the technique of runtime modification of classes is called monkeypatching, see https://en.wikipedia.org/wiki/Monkey_patch
First - here's "monkeypatch.py":
''' monkeypatch.py:
I got this from https://gist.github.com/mahmoudimus/295200 by Mahmoud Abdelkader,
his comment: "found this from Armin R. on Twitter, what a beautiful gem ;)"
I made a few changes for coding style preferences
- Rudy Albachten April 30 2015
'''
import ctypes
from types import DictProxyType, MethodType
# figure out the size of _Py_ssize_t
_Py_ssize_t = ctypes.c_int64 if hasattr(ctypes.pythonapi, 'Py_InitModule4_64') else ctypes.c_int
# python without tracing
class _PyObject(ctypes.Structure):
pass
_PyObject._fields_ = [
('ob_refcnt', _Py_ssize_t),
('ob_type', ctypes.POINTER(_PyObject))
]
# fixup for python with tracing
if object.__basicsize__ != ctypes.sizeof(_PyObject):
class _PyObject(ctypes.Structure):
pass
_PyObject._fields_ = [
('_ob_next', ctypes.POINTER(_PyObject)),
('_ob_prev', ctypes.POINTER(_PyObject)),
('ob_refcnt', _Py_ssize_t),
('ob_type', ctypes.POINTER(_PyObject))
]
class _DictProxy(_PyObject):
_fields_ = [('dict', ctypes.POINTER(_PyObject))]
def reveal_dict(proxy):
if not isinstance(proxy, DictProxyType):
raise TypeError('dictproxy expected')
dp = _DictProxy.from_address(id(proxy))
ns = {}
ctypes.pythonapi.PyDict_SetItem(ctypes.py_object(ns), ctypes.py_object(None), dp.dict)
return ns[None]
def get_class_dict(cls):
d = getattr(cls, '__dict__', None)
if d is None:
raise TypeError('given class does not have a dictionary')
if isinstance(d, DictProxyType):
return reveal_dict(d)
return d
def test():
import random
d = get_class_dict(str)
d['foo'] = lambda x: ''.join(random.choice((c.upper, c.lower))() for c in x)
print "and this is monkey patching str".foo()
if __name__ == '__main__':
test()
Here's a contrived example using monkeypatch:
I have a class "myclass" in module "mystuff" wrapped with swig -python -builtin
I want to add an extra runtime method "namelen" that returns the length of the name returned by myclass.getName()
import mystuff
import monkeypatch
# add a "namelen" method to all "myclass" objects
def namelen(self):
return len(self.getName())
d = monkeypatch.get_class_dict(mystuff.myclass)
d['namelen'] = namelen
x = mystuff.myclass("xxxxxxxx")
print "namelen:", x.namelen()
Note that this can also be used to extend or override methods on builtin python classes, as is demonstrated in the test in monkeypatch.py: it adds a method "foo" to the builtin str class that returns a copy of the original string with random upper/lower case letters
I would probably replace:
# add a "namelen" method to all "myclass" objects
def namelen(self):
return len(self.getName())
d = monkeypatch.get_class_dict(mystuff.myclass)
d['namelen'] = namelen
with
# add a "namelen" method to all "myclass" objects
monkeypatch.get_class_dict(mystuff.myclass)['namelen'] = lambda self: return len(self.getName())
to avoid extra global variables