return an object of a different class

G

Guest

How can I do something like this in python:

#!/usr/bin/python3.1

class MyNumbers:
def __init__(self, n):
self.original_value = n
if n <= 100:
self = SmallNumers(self)
else:
self = BigNumbers(self)

class SmallNumbers:
def __init__(self, n):
self.size = 'small'

class BigNumbers:
def __init__(self, n):
self.size = 'big'

t = MyNumbers(200)


When I do type(t) it says MyNumbers, while I'd want it to be BigNumbers,
because BigNumbers and SmallNumbers will have different methods etc...

Do I need to use metaclasses?

Thanks.
 
M

MRAB

How can I do something like this in python:

#!/usr/bin/python3.1

class MyNumbers:
def __init__(self, n):
self.original_value = n
if n <= 100:
self = SmallNumers(self)
else:
self = BigNumbers(self)

class SmallNumbers:
def __init__(self, n):
self.size = 'small'

class BigNumbers:
def __init__(self, n):
self.size = 'big'

t = MyNumbers(200)


When I do type(t) it says MyNumbers, while I'd want it to be BigNumbers,
because BigNumbers and SmallNumbers will have different methods etc...

Do I need to use metaclasses?
Why not just make MyNumbers a function?
 
A

aitilang

er................
I think you need a NumberFactory that makes SmallNumber and BigNumber
according to the initial value.

ÓÚ 2011-2-16 10:23, (e-mail address removed) дµÀ:
 
A

alex23

How can I do something like this in python:

#!/usr/bin/python3.1

class MyNumbers:
   def __init__(self, n):
     self.original_value = n
     if n <= 100:
       self = SmallNumers(self)
     else:
       self = BigNumbers(self)

class SmallNumbers:
   def __init__(self, n):
     self.size = 'small'

class BigNumbers:
   def __init__(self, n):
     self.size = 'big'

t = MyNumbers(200)

When I do type(t) it says MyNumbers, while I'd want it to be BigNumbers,
because BigNumbers and SmallNumbers will have different methods etc...

Firstly, does MyNumbers _have_ to be a class? Or would a function
acting as a class factory be sufficient?

Otherwise, you can change the class of an object, even within its own
methods:

class MyNumbers(object):
def __init__(self, n = 0):
self.original_value = n
self.__class__ = BigNumbers if n > 100 else SmallThing

class BigNumbers(MyNumbers):
size = 'big'

class SmallNumbers(MyNumbers):
size = 'small'
'big'

Hope this helps.
 
R

Richard Thomas

How can I do something like this in python:

#!/usr/bin/python3.1

class MyNumbers:
   def __init__(self, n):
     self.original_value = n
     if n <= 100:
       self = SmallNumers(self)
     else:
       self = BigNumbers(self)

class SmallNumbers:
   def __init__(self, n):
     self.size = 'small'

class BigNumbers:
   def __init__(self, n):
     self.size = 'big'

t = MyNumbers(200)

When I do type(t) it says MyNumbers, while I'd want it to be BigNumbers,
because BigNumbers and SmallNumbers will have different methods etc...

Do I need to use metaclasses?

Thanks.

If you don't want to use a factory function I believe you can do this:

class MyNumber(object):
def __new__(cls, n):
if n <= 100:
cls = SmallNumbers
else:
cls = BigNumbers
return object.__new__(cls, n)
...

Chard.
 
G

Guest

Firstly, does MyNumbers _have_ to be a class? Or would a function
acting as a class factory be sufficient?

Yes it does. I didn't explain my problem, chose a terrible example. This is
more what I'm trying to do:

class thingy:
def __init__(self, athingy):
self.basic_extract()
if self.sortof =

def basic_extract(self):
do a bunch of things
self.sortof = ..

def general_method(self)


class ThingyTypeA:
def __init__(self):
further_extract()

class ThingyTypeB:
def __init__(self):
further_extract()
Otherwise, you can change the class of an object, even within its own
methods:

And then I just call __init__ method?
class MyNumbers(object):
def __init__(self, n = 0):
self.original_value = n
self.__class__ = BigNumbers if n> 100 else SmallThing self.__init__()

class BigNumbers(MyNumbers): def __init__(self):
size = 'big'
self.newvalue = self.original_value * y
class SmallNumbers(MyNumbers): def __init__(self):
size = 'small'
self.newvalue = self.original_value * x
Hope this helps.

Yes, thanks!
 
G

Guest

I didn't explain my problem, chose a terrible example. This is more what I'm
trying to do:

class thingy:
def __init__(self, athingy):
self.basic_extract()
if self.typeof = A
.../...

def basic_extract(self):
# complicated logic to extract data out of the thingy here
# and set a bunch of values base on the data we extract
self.size = xxx
self.typeof = yyy
self.weight = zzz

def general_method(self)
# Method that can be used on both types of thingy

class ThingyTypeA:
def __init__(self):
# do some further extraction specific to type A
further_extract()


class ThingyTypeB:
def __init__(self):
# do some further extraction specific to type B
further_extract()
 
G

Guest

I didn't explain my problem, chose a terrible example. This is more
Basically the subclass I want to use is based on some of the data I extract
from a blob of data. If I use a function to extract the data before I create
the objects, then I need to do a bunch of assignments afterwards.
Unfortunately, it's still very contrived, and the names don't give any
suggestion as to what you're trying to achieve. Can you improve on that?


Perhaps you want those classes to inherit from your base class. Have you
done the Python tutorial? It covers inheritance and how to use it.

Yes, I have done subclasing before, and yes ThingyTypeA and B should be
subclassing Thingy
 
K

Karim

If you don't want to use a factory function I believe you can do this:

class MyNumber(object):
def __new__(cls, n):
if n<= 100:
cls = SmallNumbers
else:
cls = BigNumbers
return object.__new__(cls, n)
...

Chard.

Very beautiful code great alternative to factory method!
To memorize this pythonic way.

Regards
Karim
 
S

Steven D'Aprano

How can I do something like this in python:

#!/usr/bin/python3.1

class MyNumbers:
def __init__(self, n):
self.original_value = n
if n <= 100:
self = SmallNumers(self)
else:
self = BigNumbers(self)

(1) self is just a local variable, it isn't privileged in any way.
Assigning to the name "self" doesn't magically change the instance, so
that can't work.

(2) By the time the __init__ method is called, the instance has been
created. __init__ is the initializer, you need the constructor. Something
like this:


# Untested.
class MyNumbers:
def __new__(cls, n):
if n <= 100:
instance = SmallNumbers(n)
else:
instance = BigNumbers(n)
return instance
 
S

Steven D'Aprano

I didn't explain my problem, chose a terrible example. This is more what
I'm trying to do:
[snip "thingy" class]


No, your first example was better. This one is terrible -- it's so
generic it's meaningless.

In any case, you don't explain why it has to be a class, rather than a
factory function.
 
J

Jean-Michel Pichavant

How can I do something like this in python:

#!/usr/bin/python3.1

class MyNumbers:
def __init__(self, n):
self.original_value = n
if n <= 100:
self = SmallNumers(self)
else:
self = BigNumbers(self)

class SmallNumbers:
def __init__(self, n):
self.size = 'small'

class BigNumbers:
def __init__(self, n):
self.size = 'big'

t = MyNumbers(200)


When I do type(t) it says MyNumbers, while I'd want it to be
BigNumbers, because BigNumbers and SmallNumbers will have different
methods etc...

Do I need to use metaclasses?

Thanks.
You simply don't return inconsistent types with a return statement. This
is a general rule in programming that has probably exceptions but
regarding what you're saying, you clearly don't want to do that.

Immagine the following code:

oNumber = MyNumbers(random.int(100)) # note that oNumber is not a
MyNumbers instance... quite confusing don't you think ?
oNumber. ... wait a minute, which methods am I allowed to call ???
SmallNumbers adn BigNumbers have differents methods.

Some rules to follow, until you get some experience with python:
* use consistent types for method parameters.
* use consistent types for returned values.
* a class constructor should return an instance of that very same class.

JM
 
J

Jean-Michel Pichavant

Karim said:
[snip]
If you don't want to use a factory function I believe you can do this:

class MyNumber(object):
def __new__(cls, n):
if n<= 100:
cls = SmallNumbers
else:
cls = BigNumbers
return object.__new__(cls, n)
...

Chard.

Very beautiful code great alternative to factory method!
To memorize this pythonic way.

Regards
Karim
Do you think that the MyNumber constructor returning something else
than a MyNumber instance is the pythonic way ? It would rather be the
cryptonic way ! (haha)

JM
 
S

Steven D'Aprano

Karim said:
[snip]
If you don't want to use a factory function I believe you can do this:

class MyNumber(object):
def __new__(cls, n):
if n<= 100:
cls = SmallNumbers
else:
cls = BigNumbers
return object.__new__(cls, n)
...

Chard.

Very beautiful code great alternative to factory method! To memorize
this pythonic way.

Regards
Karim
Do you think that the MyNumber constructor returning something else
than a MyNumber instance is the pythonic way ? It would rather be the
cryptonic way ! (haha)


Support for constructors returning something other than an instance of
the class is not an accident, it is a deliberate, and useful, design. The
Fine Manual says:

object.__new__(cls[, ...])

Called to create a new instance of class cls. [...]
The return value of __new__() should be the new object
instance (usually an instance of cls).
[...]
If __new__() does not return an instance of cls, then
the new instance’s __init__() method will not be invoked.


http://docs.python.org/reference/datamodel.html#basic-customization

So while it is *usual* for the constructor to return an instance of the
class, it's not compulsory, and returning other types is explicitly
supported.

To answer your question about whether this is Pythonic... here's a small
clue from Python 2.5:
<type 'long'>



So, yes, absolutely, it is not only allowed for class constructors to
return an instance of a different class, but there is precedence in the
built-ins.
 
J

Jean-Michel Pichavant

Steven said:
Karim said:
[snip]

If you don't want to use a factory function I believe you can do this:

class MyNumber(object):
def __new__(cls, n):
if n<= 100:
cls = SmallNumbers
else:
cls = BigNumbers
return object.__new__(cls, n)
...

Chard.

Very beautiful code great alternative to factory method! To memorize
this pythonic way.

Regards
Karim
Do you think that the MyNumber constructor returning something else
than a MyNumber instance is the pythonic way ? It would rather be the
cryptonic way ! (haha)


Support for constructors returning something other than an instance of
the class is not an accident, it is a deliberate, and useful, design. The
Fine Manual says:

object.__new__(cls[, ...])

Called to create a new instance of class cls. [...]
The return value of __new__() should be the new object
instance (usually an instance of cls).
[...]
If __new__() does not return an instance of cls, then
the new instance’s __init__() method will not be invoked.


http://docs.python.org/reference/datamodel.html#basic-customization

So while it is *usual* for the constructor to return an instance of the
class, it's not compulsory, and returning other types is explicitly
supported.

To answer your question about whether this is Pythonic... here's a small
clue from Python 2.5:

<type 'long'>



So, yes, absolutely, it is not only allowed for class constructors to
return an instance of a different class, but there is precedence in the
built-ins.
Returning another class instance in a new operator is legit, but
regarding the documentaiton you quoted, it's also "unusual", for which
is used the word "cryptonic" instead. But that was only in the attempt
of being clever. But I'm used to that when it comes to joking in english
:-/.

Thing is, I said the same thing that the __new__ documentation.

Concerning the example you've given, you shoud notice that int and long
objects have the exact same interface, same attributes. And all their
methods mean to do the same thing. In fact it doesn't really matter if
you're manipulating an int or a long, you're still using the 2 objects
the same way for the same result. From a duck typing POV, they're the
same class.
This is not what would happen for the OP, given the examples he gave.

JM
 
W

Westley Martínez

Karim said:
[snip]
If you don't want to use a factory function I believe you can do this:

class MyNumber(object):
def __new__(cls, n):
if n<= 100:
cls = SmallNumbers
else:
cls = BigNumbers
return object.__new__(cls, n)
...

Chard.

Very beautiful code great alternative to factory method! To memorize
this pythonic way.

Regards
Karim
Do you think that the MyNumber constructor returning something else
than a MyNumber instance is the pythonic way ? It would rather be the
cryptonic way ! (haha)


Support for constructors returning something other than an instance of
the class is not an accident, it is a deliberate, and useful, design. The
Fine Manual says:

object.__new__(cls[, ...])

Called to create a new instance of class cls. [...]
The return value of __new__() should be the new object
instance (usually an instance of cls).
[...]
If __new__() does not return an instance of cls, then
the new instance’s __init__() method will not be invoked.


http://docs.python.org/reference/datamodel.html#basic-customization

So while it is *usual* for the constructor to return an instance of the
class, it's not compulsory, and returning other types is explicitly
supported.

To answer your question about whether this is Pythonic... here's a small
clue from Python 2.5:
<type 'long'>



So, yes, absolutely, it is not only allowed for class constructors to
return an instance of a different class, but there is precedence in the
built-ins.
Python 3 removed longs because they were ... cryptonic!
 
M

MRAB

Karim wrote:
[snip]
If you don't want to use a factory function I believe you can do this:

class MyNumber(object):
def __new__(cls, n):
if n<= 100:
cls = SmallNumbers
else:
cls = BigNumbers
return object.__new__(cls, n)
...

Chard.

Very beautiful code great alternative to factory method! To memorize
this pythonic way.

Regards
Karim
Do you think that the MyNumber constructor returning something else
than a MyNumber instance is the pythonic way ? It would rather be the
cryptonic way ! (haha)


Support for constructors returning something other than an instance of
the class is not an accident, it is a deliberate, and useful, design. The
Fine Manual says:

object.__new__(cls[, ...])

Called to create a new instance of class cls. [...]
The return value of __new__() should be the new object
instance (usually an instance of cls).
[...]
If __new__() does not return an instance of cls, then
the new instance’s __init__() method will not be invoked.


http://docs.python.org/reference/datamodel.html#basic-customization

So while it is *usual* for the constructor to return an instance of the
class, it's not compulsory, and returning other types is explicitly
supported.

To answer your question about whether this is Pythonic... here's a small
clue from Python 2.5:
n = int("4294967296") # 2**32
type(n)
<type 'long'>



So, yes, absolutely, it is not only allowed for class constructors to
return an instance of a different class, but there is precedence in the
built-ins.
Python 3 removed longs because they were ... cryptonic!
Strictly speaking, they weren't removed. ints were removed and long was
renamed int.
 
W

Westley Martínez

On Thu, 17 Feb 2011 12:02:28 +0100, Jean-Michel Pichavant wrote:

Karim wrote:
[snip]
If you don't want to use a factory function I believe you can do this:

class MyNumber(object):
def __new__(cls, n):
if n<= 100:
cls = SmallNumbers
else:
cls = BigNumbers
return object.__new__(cls, n)
...

Chard.

Very beautiful code great alternative to factory method! To memorize
this pythonic way.

Regards
Karim
Do you think that the MyNumber constructor returning something else
than a MyNumber instance is the pythonic way ? It would rather be the
cryptonic way ! (haha)


Support for constructors returning something other than an instance of
the class is not an accident, it is a deliberate, and useful, design. The
Fine Manual says:

object.__new__(cls[, ...])

Called to create a new instance of class cls. [...]
The return value of __new__() should be the new object
instance (usually an instance of cls).
[...]
If __new__() does not return an instance of cls, then
the new instance’s __init__() method will not be invoked.


http://docs.python.org/reference/datamodel.html#basic-customization

So while it is *usual* for the constructor to return an instance of the
class, it's not compulsory, and returning other types is explicitly
supported.

To answer your question about whether this is Pythonic... here's a small
clue from Python 2.5:

n = int("4294967296") # 2**32
type(n)
<type 'long'>



So, yes, absolutely, it is not only allowed for class constructors to
return an instance of a different class, but there is precedence in the
built-ins.
Python 3 removed longs because they were ... cryptonic!
Strictly speaking, they weren't removed. ints were removed and long was
renamed int.
My point stands.
 
A

alex23

Jean-Michel Pichavant said:
You simply don't return inconsistent types with a return statement. This
is a general rule in programming that has probably exceptions but
regarding what you're saying, you clearly don't want to do that.

I don't think they were intended to be inconsistent types, but
subclasses of the same type. Returning different subclasses is exactly
what a factory is for. And given Python's propensity for duck typing,
they don't even really need to be subclassed for the same object, they
just need the interface that is required.
Immagine the following code:

oNumber = MyNumbers(random.int(100)) # note that oNumber is not a
MyNumbers instance... quite confusing don't you think ?
oNumber. ... wait a minute, which methods am I allowed to call ???
SmallNumbers adn BigNumbers have differents methods.

What if they have different implementations rather than methods?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top