constructing an object from another instance of the same class
Bruno Desthuilliers
bruno.42.desthuilliers at websiteburo.invalid
Fri Jun 18 11:08:23 EDT 2010
Christoph Groth a écrit :
> Bruno Desthuilliers <bruno.42.desthuilliers at websiteburo.invalid> writes:
>
>>> It seems to me that in this way I might get problems when I pass an
>>> instance of Derived_from_my_type to bar, as it will become an
>>> instance of My_type.
>> The instance you pass to bar won't "become" anything else. You create
>> a new My_type instance from the Derived_from_my_type one's values, and
>> rebinding the _local_ name 'arg' only affects the local namespace.
>
> I understand that it won't become an instance of My_type globally. But
> it will become locally and this breaks polymorphism.
Then don't do it !-)
> (See code example
> I provide at the end)
>
>>> In C++
>> Forget about C++ - Python is a different beast !-)
>
> Still, it is useful and interesting to compare languages.
Indeed. But you have to understand enough of a language to compare it
with another one. The old "I can write C in any language" syndrom...
>>> (which I know better than python) I would make bar accept a const
>>> reference to My_type. Then I could use it directly with instances of
>>> My_type, Derived_from_my_type and other types which can be converted
>>> into My_type.
>> If you only worry about being able to use any "My_type like" object -
>> that is, any object implementing a given subset of My_type's
>> interface, then just document your expectations in the function's
>> docstring and use whatever object is passed in as if it was a My_type
>> instance. Period. As long as you document what your function expects,
>> it's the caller's responsaibility to make sure it provides something
>> compatible. If he don't, well he'll get a nice traceback.
>
> This is not what I am worrying about. I will try to be more explicit.
Ok.
> I would like to have a class for a "special matrix". This is an
> ordinary 2n by 2n matrix which can be thought of as consisting of four n
> by n sized blocks.
Right.
> At this moment, I just use normal numpy arrays (or anything which
> behaves like them). But I have very good reasons to actually have a
> class for these special matrices. Still, I would like to be able to
> call functions which work with "special matrices" with plain numpy
> arrays as arguments. In that case, the arguments which are plain
> matrices should be converted to "special" ones such that the main part
> of the function can assume to always work with "special matrices".
Ok. So you want to build a "special matrice" like object from the numpy
array.
> The code attached in the end (which is a complete runnable script)
> should illustrate what I mean.
>
> This example works as I want except that when bar is called with a an
> argument of type Derived, it behaves as Base.
Ok.
> Also, I am not sure
> whether there is a nicer way to achieve the following functionality for
> Base.__init__:
>
> If other is of type Base already, just "pass it on". Otherwise,
> construct an instance of Base from it.
You can't do this in the initializer, you have to use either a factory
function or the proper constructor (or an alternate constructor).
> ****************************************************************
> import numpy as np
>
> class Base:
If you're using Python 2.x, make this:
class Base(object):
> def __init__(self, other):
> if isinstance(other, type(self)):
> self = other
'self' is a local name. Rebinding a local name only impact the local
namespace.
> return
> n = other.shape[0]
> assert n == other.shape[1]
> assert n % 2 == 0
> n //= 2
> self.a = other[0 : n, 0 : n]
> self.b = other[n : 2*n, 0 : n]
> self.c = other[0 : n, n : 2*n]
> self.d = other[n : 2*n, n : 2*n]
Question : is there any case where you may want to instanciate this
class with explicit values for a, b, c and d ?
Anyway: the simplest solution here is to replace the call to your Base
class with a call to a factory function. I'd probably go for something
like (Q&D untested code and other usual warnings) :
class Base(object):
def __init__(self, a, b, c, d):
self.a = a
self.b = b
self.c = c
self.d = d
@classmethod
def from_array(cls, arr):
""" alternate constructor from a numpy array """
n = arr.shape[0]
assert n == arr.shape[1]
assert n % 2 == 0
n //= 2
return cls(
arr[0 : n, 0 : n],
arr[n : 2*n, 0 : n],
arr[0 : n, n : 2*n],
arr[n : 2*n, n : 2*n]
)
def hello(self):
print 'hello from Base'
class Derived(Base):
def hello(self):
print 'hello from Derived'
def coerce(obj, cls=Base):
if isinstance(obj, cls):
return obj
else:
return cls.from_array(obj)
def bar(arg):
arg = coerce(arg)
# Do something useful with arg.{a..d}
arg.hello()
# This works.
a = np.identity(4)
bar(a)
# And this also.
a = Base.from_array(np.identity(4))
bar(a)
# And now this should work too
a = Derived.from_array(np.identity(4))
bar(a)
Does it solve your problem ?-)
Note that if using a plain function hurts your feelings - or just isn't
practical for any other reason - you can also make it another
classmethod of Base, ie :
class Base(object):
def __init__(self, a, b, c, d):
self.a = a
self.b = b
self.c = c
self.d = d
@classmethod
def from_array(cls, arr):
""" alternate constructor from a numpy array """
n = arr.shape[0]
assert n == arr.shape[1]
assert n % 2 == 0
n //= 2
return cls(
arr[0 : n, 0 : n],
arr[n : 2*n, 0 : n],
arr[0 : n, n : 2*n],
arr[n : 2*n, n : 2*n]
)
@classmethod
def coerce(cls, obj):
if isinstance(obj, cls):
return obj
else:
return cls.from_array(obj)
def bar(arg):
arg = Base.coerce(arg)
# Do something useful with arg.{a..d}
arg.hello()
HTH
More information about the Python-list
mailing list