Simple questions on use of objects (probably faq)

Steven D'Aprano steve at REMOVETHIScyber.com.au
Wed Mar 8 12:27:50 CET 2006


On Wed, 08 Mar 2006 11:04:41 +0100, Brian Elmegaard wrote:

> 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]

>>> p = Parrot("    beautiful plumage    ")
All parrots have beautiful plumage.
>>> p.y
'B'
>>> p.x
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]

>>> obj = Spam(2, 3, 4)
>>> obj.lister()
[2, 4, -3]

Now you can do anything you want with it:

>>> max(obj.lister())
4
>>> min(obj.lister())
-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])

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[i] 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,



-- 
Steven.




More information about the Python-list mailing list