Simple questions on use of objects (probably faq)

  • Thread starter Brian Elmegaard
  • Start date
B

Brian Elmegaard

Hi,

I am struggling to understand how to really appreciate object
orientation. I guess these are FAQ's but I have not been able to find
the answers. Maybe my problem is that my style and understanding are
influenced by matlab and fortran.

I tried with the simple example below and ran into several questions:
1: Why can't I do:
def __init__(self,self.x):
and avoid the self.x=x

2: Is it a good idea to insert instances in a list or is there a simpler
way to do something with all instances of a given type?

3: Why canøt I say and get the maximum of instance attributes and a
list of them?
y_max=max(y[].x) and
ys=[y[].x]

4: Can I avoid the dummy counter i in the for loop and do something
like:
yz=[y[:-1].x-y[1:].x]

The code that runs:

class Foo:
def __init__(self,x):
self.x=x

y=[]
y.append(Foo(10.0))
y.append(Foo(110.0))
y.append(Foo(60.0))

ys=[]
y_max=0.0
y_min=0.0

for s in y:
ys.extend([s.x])
y_max=max(s.x,y_max)
y_min=min(s.x,y_min)

yz=[]
for i in range(len(ys)-1):
yz.append(ys[i+1]-ys)

What I hoped I could do:
class Foo:
def __init__(self,self.x):
continue
y=[]
y.append(Foo(10.0))
y.append(Foo(110.0))
y.append(Foo(60.0))

ys=([y[].x])
y_max=max(y[].x)
y_min=min(y[].x)

yz=[y[:-1].x-y[1:].x]
 
M

Matt Hammond

Hi,
3: Why canøt I say and get the maximum of instance attributes and a
list of them?
y_max=max(y[].x) and
ys=[y[].x]

y_max = max([e.x for e in y])

See "List comprehensions" in python docs:
http://docs.python.org/tut/node7.html#SECTION007140000000000000000
4: Can I avoid the dummy counter i in the for loop and do something
like:
yz=[y[:-1].x-y[1:].x]

yz = [e.x for e in y]
yz.reverse()

--

| Matt Hammond
| R&D Engineer, BBC Research & Development, Tadworth, Surrey, UK.
| http://kamaelia.sf.net/
| http://www.bbc.co.uk/rd/
 
S

Steven D'Aprano

Hi,

I am struggling to understand how to really appreciate object
orientation. I guess these are FAQ's but I have not been able to find
the answers. Maybe my problem is that my style and understanding are
influenced by matlab and fortran.

I tried with the simple example below and ran into several questions:
1: Why can't I do:
def __init__(self,self.x):
and avoid the self.x=x

Okay, let's look at an example:

class Parrot:
def __init__(self, self.x):
pass

Now we create a new instance of Parrot:

p = Parrot("beautiful plumage")

What happens is that Python creates a Parrot instance, and calls the
__init__ method with two arguments: the instance itself, called "self",
and the string "beautiful plumage" called "self.x".

Except, this won't work. You can't define a method or function definition
like this:

def f(x.y): # won't work

This is a syntax error, and it would require significant changes to
Python to make it work -- changes which are of dubious benefit.

Okay, so what about an alternative:

class Parrot:
def __init__(self, x):
# Python automatically calls "self.x = x",
# so you don't have to.
pass

Why doesn't Python do this?

The answer is simple. What if you don't want x to be an attribute of the
instance? What if you want to use x some other way?

class Parrot:
def __init__(self, x):
y = x.strip()
print "All parrots have %s." % y
self.y = y.upper()[0]
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: Parrot instance has no attribute 'x'

As a general rule, Python never guesses what you want. It is better to be
explicit than implicit. If you want an attribute self.x, you have to
assign to it, Python won't guess that just because you pass an argument x
to __init__ that it should be turned into an attribute.


2: Is it a good idea to insert instances in a list or is there a simpler
way to do something with all instances of a given type?

If you want to do something with a number of instances, you need to keep
track of them somehow. You can do that by assigning each instance to its
own name:

p1 = Parrot("sharp beaks")
p2 = Parrot("long tail feathers")
p3 = Parrot("an appetite for nuts")

Or in a list:

parrots = [Parrot("sharp beaks"), Parrot("long tail feathers")]

or a dictionary:

parrots = {1: Parrot("sharp beaks"), 2: Parrot("long tail feathers")}

or any other way you like.


3: Why canøt I say and get the maximum of instance attributes and a
list of them?
y_max=max(y[].x) and
ys=[y[].x]

If doesn't make sense to talk about getting the maximum of instance
attributes. What if some attributes are numbers and some are not? How does
Python know which attributes you care about?

Of course you can, if you program your class to do it.

class Spam:
def __init__(self, x, y, z):
self.x = x
self.y = y + 1
self.z = 1 - z
self.args = (x, y, z) # save a copy of the arguments

def lister(self):
# no need to report self.args
return [self.x, self.y. self.z]
[2, 4, -3]

Now you can do anything you want with it:
-3



4: Can I avoid the dummy counter i in the for loop and do something
like:
yz=[y[:-1].x-y[1:].x]

Probably. If I work out what you are trying to do, I'll answer.


The code that runs:

class Foo:
def __init__(self,x):
self.x=x

y=[]
y.append(Foo(10.0))
y.append(Foo(110.0))
y.append(Foo(60.0))

Okay, you have a list of Foo instances.
ys=[]
y_max=0.0
y_min=0.0

for s in y:
ys.extend([s.x])

You don't need to create a new, single item list and call the extend
method. Do this instead:

ys.append(s.x)
y_max=max(s.x,y_max)
y_min=min(s.x,y_min)

Unless you actually want to see the maximum and minimum as they change,
this is wasteful. Just call the function at the end, after collecting all
the values:

y_max = max(ys)
y_min = min(ys)

yz=[]
for i in range(len(ys)-1):
yz.append(ys[i+1]-ys)


I think you are creating a list of first differences, yes?

Your code should work, and is perfectly fine. Here is another way:

# use a list comprehension:
yz = [ys[i+1] - ys for i in range(len(ys) - 1)]

And another:

for index, value in enumerate(ys[:-1]):
yz.append(ys[index+1] - value)


By the way, don't be shy about using more meaningful names for variables.
ys and yz are terribly similar, and is a bug waiting to happen.


What I hoped I could do:
class Foo:
def __init__(self,self.x):
continue

You can't use continue in there, it isn't a null-op. Perhaps you wanted
"pass"?
y=[]
y.append(Foo(10.0))
y.append(Foo(110.0))
y.append(Foo(60.0))

ys=([y[].x])
y_max=max(y[].x)
y_min=min(y[].x)

yz=[y[:-1].x-y[1:].x]

How about, before trying to invent short cuts, you actually learn some of
the Python syntax? The [x:y] syntax already has a meaning to Python,
just not what you want.

Also, while everything in Python is an object, you don't *have* to use
object oriented techniques. I suggest you spend some time playing with
Python in the interactive interpreter, just doing things with lists of
numbers, strings, and so forth, getting used to how they work, learning
the language. The help() command is very useful, and if you haven't done
the tutorial, you should.


Hope this helps,
 
S

Steven D'Aprano

4: Can I avoid the dummy counter i in the for loop and do something
like:
yz=[y[:-1].x-y[1:].x]

yz = [e.x for e in y]
yz.reverse()

I don't think that's what the O.P. actually wants. He seems to have
misused slicing syntax as some sort of weird replacement for a for loop.

Of course, I could be wrong.
 
M

Matt Hammond

4: Can I avoid the dummy counter i in the for loop and do something
like:
yz=[y[:-1].x-y[1:].x]

yz = [e.x for e in y]
yz.reverse()

I don't think that's what the O.P. actually wants. He seems to have
misused slicing syntax as some sort of weird replacement for a for loop.

Of course, I could be wrong.

Hmmm, rereading, I think you're right ... and I think I'm confused too :)

Attempt #2:

yz = [ (y1.x - y2.x) for (y1,y2) in zip(y[:-1], y[1:]) ]

Frankly, a for loop with an index would probably be easier to read :)



Matt
--

| Matt Hammond
| R&D Engineer, BBC Research & Development, Tadworth, Surrey, UK.
| http://kamaelia.sf.net/
| http://www.bbc.co.uk/rd/
 
B

Brian Elmegaard

Matt Hammond said:
y_max = max([e.x for e in y])

Would there be a way to refer back to the e with maximum x, or how
could I find other attributes of it?
 
M

Max M

Brian said:
Hi,

I am struggling to understand how to really appreciate object
orientation. I guess these are FAQ's but I have not been able to find
the answers. Maybe my problem is that my style and understanding are
influenced by matlab and fortran.
What I hoped I could do:
class Foo:
def __init__(self,self.x):
continue
y=[]
y.append(Foo(10.0))
y.append(Foo(110.0))
y.append(Foo(60.0))

ys=([y[].x])
y_max=max(y[].x)
y_min=min(y[].x)

yz=[y[:-1].x-y[1:].x]

It is hard to tell what you are trying to do here. But here is a shot at
parts of the code.

class Foo:
def __init__(self, x):
self.x = x

y = [Foo(10.0), Foo(110.0), Foo(60.0)]
x_values = [o.x for o in y]
y_max = max(x_values)
y_min = min(x_values)


Otherwise you could try and describe with words what you are getting at.


--

hilsen/regards Max M, Denmark

http://www.mxm.dk/
IT's Mad Science
 
B

Brian Elmegaard

Thanks for the answers. They are very useful.
self.args = (x, y, z) # save a copy of the arguments

As always python makes it easy.

Actually I wanted to get the maximum of attributes of several
instances. List comprehension is the answer.
method. Do this instead:

ys.append(s.x)

I always get confused by extend and append.
this is wasteful. Just call the function at the end, after collecting all
the values:

Easier indeed.
for index, value in enumerate(ys[:-1]):
yz.append(ys[index+1] - value)

I will need to study enumerate a bit.
By the way, don't be shy about using more meaningful names for variables.
ys and yz are terribly similar, and is a bug waiting to happen.

I know, and in the real code I use better names.
You can't use continue in there, it isn't a null-op. Perhaps you wanted
"pass"?
Yes.
yz=[y[:-1].x-y[1:].x]

How about, before trying to invent short cuts, you actually learn some of
the Python syntax? The [x:y] syntax already has a meaning to Python,
just not what you want.

Perhaps it is not the same, but quite close. In matlab .* is
element-by-element multiplication. I was thinking about a .-
operator. wouldn't that make sense here?
Also, while everything in Python is an object, you don't *have* to use
object oriented techniques.

In the real problem the class is:
class Stream:
def __init__(self,T_start,T_end,Q_dot):
self.T_start=T_start
self.T_end=T_end
self.Q_dot=Q_dot
self.mcp=abs(Q_dot/(T_start-T_end))
if T_start>T_end:
self.type='hot'
else:
self.type='cold'

and I thought it would make sense to store this a objects. Otherwise I
would need to store each stream as a list is refer their indexes.
 
M

Max M

Brian said:
y_max = max([e.x for e in y])


Would there be a way to refer back to the e with maximum x, or how
could I find other attributes of it?


In that case a common idiom is to "decorate"


decorated = [(obj.x, obj) for obj in objects]
max_decorated = max(decorated)
max_obj = max_decorated[-1]


Or to run through it "old style"

max_obj = objects[0]
for obj in objects:
if obj.x > max_obj.x:
max_obj = obj


Readbility is about the same I think. Testing should tell you which is
faster in your case.

--

hilsen/regards Max M, Denmark

http://www.mxm.dk/
IT's Mad Science
 
B

Brian Elmegaard

Matt Hammond said:
Hmmm, rereading, I think you're right ... and I think I'm confused too :)

You both are.
Attempt #2:

yz = [ (y1.x - y2.x) for (y1,y2) in zip(y[:-1], y[1:]) ]

Frankly, a for loop with an index would probably be easier to read :)

Me too, would that be what I already had?
 
B

bruno at modulix

Brian said:
Hi,

I am struggling to understand how to really appreciate object
orientation. I guess these are FAQ's but I have not been able to find
the answers. Maybe my problem is that my style and understanding are
influenced by matlab and fortran.

I tried with the simple example below and ran into several questions:
1: Why can't I do:
def __init__(self,self.x):
and avoid the self.x=x

Before you assign a value to it, self.x does not exists.
2: Is it a good idea to insert instances in a list or is there a simpler
way to do something with all instances of a given type?

Depends on your program. But there are at least ways to make this a bit
more transparent, see below.
3: Why canøt I say and get the maximum of instance attributes and a
list of them?
y_max=max(y[].x) and

y_max = max([s.x for s in y])
ys=[y[].x]

ys = [s.x for s in y]
4: Can I avoid the dummy counter i in the for loop and do something
like:
yz=[y[:-1].x-y[1:].x]

based on the other snippet you posted:
yz=[]
for i in range(len(ys)-1):
yz.append(ys[i+1]-ys)


yz = [next - current for next, current in zip(ys[1:], ys[0:-1])]

I'm not sure this is exactly what you want, but this should get you
started anyway.


Now how you could do it the OO way (Q&D, not really tested):

class Foo(object):
_instances = []

def __init__(self, x):
self.x = x # no, you won't avoid it
self._add_instance(self)

@classmethod
def _add_instance(cls, instance):
cls._instances.append(instance)

@classmethod
def get_instances(cls):
return cls._instances[:]

@classmethod
def get_values(cls):
return [i.x for i in cls.get_instances()]

@classmethod
def max(cls):
return max(cls.get_values())

@classmethod
def min(cls):
return min(cls.get_values())

@classmethod
def strange_computation(cls):
values = cls.get_values()
return [next - current \
for next, current in zip(values[1:], values[:-1])]


for value in [10.0, 110.0, 60.0]:
Foo(value)

Foo.get_values()
Foo.max()
Foo.min()
Foo.strange_computation()


HTH
 
B

Brian Elmegaard

bruno at modulix said:
Now how you could do it the OO way (Q&D, not really tested):

Something goes wrong in my 2.3 when I change the syntax to
_add_instance=classmethod(_add_instance).

If I understand this correctly the class is keeping track of the
instances of itself. The class is extendible and has all the needed
methods. This means that any global lists can be
avoided. Interesting.
 
J

James Stroud

Brian said:
y_max = max([e.x for e in y])


Would there be a way to refer back to the e with maximum x, or how
could I find other attributes of it?

You should look into __cmp__ and other magic methods. This is probably
the type of functionality you seem to be after.

class C:
def __init__(self, x):
self.x = x
def __repr__(self):
idnum = str(id(self))[-4:] #
return "C(x=%s):%s" % (self.x, idnum) # for demo
def __cmp__(self, other):
return self.x - other.x

# demonstration
import random
sees = [C(x) for x in (4,7,1,3,0,9,2)] # random-ish ints
print sees
print max(sees)
sees.sort()
print sees

James

--
James Stroud
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA 90095

http://www.jamesstroud.com/
 
B

bruno at modulix

Brian said:
Something goes wrong in my 2.3

So it's time to move to 2.4x !-)

What is "going wrong" exactly ?
when I change the syntax to
_add_instance=classmethod(_add_instance).
If I understand this correctly the class is keeping track of the
instances of itself.
Yeps.

The class is extendible and has all the needed
methods. This means that any global lists can be
avoided.

Yeps too. This is called "encapsulation".

But this also means you only have one list of instances - which may or
may not be ok for what you're trying to do. Note that there are possible
workarounds, like using a dict of lists:

class Foo(object):
_instance_lists = {}

def __init__(self, x, list_id):
self.x = x # no, you won't avoid it
self._add_instance(self, list_id)

@classmethod
def _add_instance(cls, instance, list_id):
cls._instance_lists.setdefault(list_id, []).append(instance)

@classmethod
def get_instances(cls, list_id):
return cls._instance_lists[list_id].[:]

# now you need to add the list_id parameter to all others classmethods

Also, if you intend to use such a solution (with or without multiple
lists), you may want to add a classmethod to delete instances from the
list(s).
 
B

Brian Elmegaard

bruno at modulix said:
So it's time to move to 2.4x !-)

I guess so.

What is "going wrong" exactly ?

def _add_instance(cls, instance):
_add_instance=classmethod(_add_instance)
cls._instances.append(instance)

gives me:
d:/DTU/80494 $ python.exe ooo.py
Traceback (most recent call last):
File "ooo.py", line 36, in ?
Foo(value)
File "ooo.py", line 6, in __init__
self._add_instance(self)
File "ooo.py", line 9, in _add_instance
_add_instance=classmethod(_add_instance)
UnboundLocalError: local variable '_add_instance' referenced before assignment
d:/DTU/80494 $
Yeps too. This is called "encapsulation".

Interesting.
Also, if you intend to use such a solution (with or without multiple
lists), you may want to add a classmethod to delete instances from the
list(s).

I will have to study classmethods.
 
B

Brian Elmegaard

James Stroud said:
You should look into __cmp__ and other magic methods. This is probably
the type of functionality you seem to be after.

Good example, I need to look at the magic methods.
What I want is to get the value of another variable in C. Would I need to
use __repr__ and get the id from it to find xx of the instance with
maximum x?

class C:
def __init__(self, x):
self.x = x
self.xx = x*x

sees = [C(x) for x in (4,7,1,3,0,9,2)] # random-ish ints
print max(sees)
 
S

Steven D'Aprano

James Stroud said:
You should look into __cmp__ and other magic methods. This is probably
the type of functionality you seem to be after.

Good example, I need to look at the magic methods.
What I want is to get the value of another variable in C. Would I need to
use __repr__ and get the id from it to find xx of the instance with
maximum x?

class C:
def __init__(self, x):
self.x = x
self.xx = x*x

sees = [C(x) for x in (4,7,1,3,0,9,2)] # random-ish ints
print max(sees)


Can you explain more carefully what you are trying to do? If you want the
square of the maximum value, just do this:

max(4,7,1,3,0,9,2)**2


__repr__ is *not* the way to get the ID of a general object:
'4'

For some objects, the default representation includes the ID as part of
the representation string, but that's not the way to get the ID. The way
to do that is with the id() function.

Right, now that you've learnt how to get the ID of an object, scrub it
from your mind. You won't need it. You can't use it. It isn't the way to
do whatever you are trying to do, whatever it is that you are trying to
do. These aren't the droids you are looking for.

[The above paragraph is always true, except for the exceptions when it is
not true. You will know when you need id(). If you only *think* you need
id(), you don't need it.]

Seriously though, you can't generally work backwards to see where an
object came from (which list, which dictionary, whatever). In general,
objects do not know what references there are to that object. If your
algorithm relies on object X knowing that it is the 5th item of list L,
then you must either store that information yourself somewhere, and
maintain it, or you must change your algorithm.
 
S

Steven D'Aprano

def _add_instance(cls, instance):
_add_instance=classmethod(_add_instance)
cls._instances.append(instance)

gives me:
d:/DTU/80494 $ python.exe ooo.py
Traceback (most recent call last):
File "ooo.py", line 36, in ?
Foo(value)
File "ooo.py", line 6, in __init__
self._add_instance(self)
File "ooo.py", line 9, in _add_instance
_add_instance=classmethod(_add_instance)
UnboundLocalError: local variable '_add_instance' referenced before assignment

This isn't a bug in version 2.3. It is a bug in your code. Follow the
code at runtime when you call the method:

Calling x._add_instance(foo) calls the _add_instance method with arguments
x.__class__ and foo. The first instruction that gets executed is:

_add_instance=classmethod(_add_instance)

but _add_instance doesn't exist inside the method's local scope, so you
get an UnboundLocalError exception.

What you probably think you want is something like this:

class Foo:
_instances = []

def _add_instance(cls, instance):
cls._instances.append(instance)

_add_instances = classmethod(_add_instances)


I say "think you want" because I don't know what problem you are trying to
solve with this messy, self-referential, piece of code. If you could
explain what your goal is, there is probably a better way of reaching it.
 
B

Brian Elmegaard

Steven D'Aprano said:
Can you explain more carefully what you are trying to do? If you want the
square of the maximum value, just do this:

I want to get the value of another attribute of the instance with
maximum x.

I know I could do it like you suggest for the case with x*x, but the
other attribute does not depend on x.
[The above paragraph is always true, except for the exceptions when it is
not true. You will know when you need id(). If you only *think* you need
id(), you don't need it.]
:)

algorithm relies on object X knowing that it is the 5th item of list L,
then you must either store that information yourself somewhere, and
maintain it, or you must change your algorithm.

OK, I think this is the answer I was looking for.
 

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

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top