OO in Python? ^^

F

Fredrik Lundh

Matthias said:
I really see issues with this, can anyone comment on this who has been
working with Python more than just a day (like me)?

Maybe you should work with Python more than one day before you
start looking for potential problems? ;-)

(I suggest reimplementing some portion of some C++ program you've
worked on recently, to get a feel for the language.)

For any potential problem you can think of, you'll find people here
who've never ever had any problems with it, and you'll find people
who think that this specific problem is what prevents Python from
going "mainstream" (and who love when someone else seems to
support their view, whether they really do it or not).

FWIW, having worked full time in and on Python for over 10 years, I
can assure you that I don't have a problem with:

- mistyped variable names
- indentation ("SUCKS BIG TIME")
- how to handle import statements
- finding things in the library reference
- the blue color on python.org
- the size of the python DLL on windows
- tabs vs. spaces
- unnecessary colons
- lambdas being removed in python 3.0
- lambdas not being removed in python 3.0
- limited support for GIF animation in PIL
- the noise level on comp.lang.python
- the global interpreter lock
- the unsuitability of notepad as a programmer editor
- the number of web frameworks available

or any of the other "major" problems that you'll hear about on c.l.python
from time to time. But that's me. Your milage may vary. The only way
to find out is to use the language. Write code, not usenet posts.

</F>
 
A

Aahz

Another thing which is really bugging me about this whole dynamically
typing thing is that it seems very error prone to me:

foo = "some string!"

# ...

if (something_fubar):
fo = "another string"

Oops, the last 'o' slipped, now we have a different object and the
interpreter will happily continue executing the flawed program.

pychecker (or pylint, but I haven't tried that)
 
S

Steven D'Aprano

Any language would be benefited from test-driven development, python
needs it because of its dynamic nature.

We can use "need" in the strict sense, as in "the language won't work
without it". I think we can reject that as too strong, because clearly
Python can work without unittests or any other sort of testing.

In the looser sense of "this will benefit you", I think it is fair to say
that *all* languages need test-driven development. If you want your code
to do some non-trivial task X, and you don't actually test to see if
it really does do X, then all the compiler tests in the world won't tell
you that your code is doing X.

Of course, the IT world is full of people writing code and not testing
it, or at least not testing it correctly. That's why there are frequent
updates or upgrades to software that break features that worked in the
older version. That would be impossible in a test-driven methodology, at
least impossible to do by accident.


And I don't think Haskell make the programmer do a lot of work(just
because of its static type checking at compile time).

I could be wrong, but I think Haskell is *strongly* typed (just like
Python), not *statically* typed. At least the "What Is Haskell?" page at
haskell.org describes the language as strongly typed, non-strict, and
allowing polymorphic typing.
 
B

bonono

Steven said:
I could be wrong, but I think Haskell is *strongly* typed (just like
Python), not *statically* typed. At least the "What Is Haskell?" page at
haskell.org describes the language as strongly typed, non-strict, and
allowing polymorphic typing.
What is your definition of statically typed ? The non-strict as far as
I know is not referring to type checking. It does check type at compile
time though it is quite different from language like C, Java, the
polymorphic typing.
 
M

Matthias Kaeppler

gene said:

First of all, thanks everybody for posting all these answers and links,
much appreciated. What Bruce Eckel wrote about dynamic typing was quite
convincing and reasonable.

I stumbled over this paragraph in "Python is not Java", can anyone
elaborate on it:

"In Java, you have to use getters and setters because using public
fields gives you no opportunity to go back and change your mind later to
using getters and setters. So in Java, you might as well get the chore
out of the way up front. In Python, this is silly, because you can start
with a normal attribute and change your mind at any time, without
affecting any clients of the class. So, don't write getters and setters."

Why would I want to use an attribute in Python, where I would use
getters and setters in Java? I know that encapsulation is actually just
a hack in Python (common, "hiding" an implementation detail by prefixing
it with the classname so you can't access it by its name anymore? Gimme
a break...), but is that a reason to only write white box classes? ^^

- Matthias
 
S

Steven D'Aprano

What is your definition of statically typed ? The non-strict as far as
I know is not referring to type checking. It does check type at compile
time though it is quite different from language like C, Java, the
polymorphic typing.

Strongly typed means that objects have a type. All objects in Python have
a type.

Strongly typed languages like Python forbid you from performing operations
on mismatched types, e.g. 1 + "1" does not work. In order to perform
operations on mismatched types, you must explicitly perform a conversion,
e.g. 1 + int("1").

Weakly typed languages do not prevent you performing operations on
mismatched types, e.g. something like 1 + "1" is allowed in languages like
Basic and Perl.

Untyped languages do not have any type information at all -- everything
is just bytes. The most obvious example is assembly language.

It should be noted that strong and weak typing is a matter of degree:
despite being mostly strongly typed, Python does do automatic coercion of
ints and floats, and although it is (arguably) weakly typed, Perl won't
allow you to treat scalars as arrays or vice versa.

Dynamic typing means that variables can be dynamically set to objects of
wildly different types. For Python, we would say that any name can be
bound to any object of any type.

Static typing is the opposite of dynamic typing. Once a variable or
name is defined as a certain type (either by a declaration, or
implicitly the first time it is used), it can only be assigned to values
of that same type.

These two articles may be helpful:

http://www.voidspace.org.uk/python/articles/duck_typing.shtml
http://www.artima.com/forums/flat.jsp?forum=106&thread=7590


A thoughtful defence of static typing is here:

http://www.xoltar.org/misc/static_typing_eckel.html

The fact that it is sub-titled "How Java/C++/C# Ruin Static Typing for the
Rest of Us" should give some idea what it is about.
 
P

Paul Boddie

Heiko said:
Maybe I'm assuming things by thinking that others also follow my line of
thought, but I've actually had very positive responses so far when telling
people that a certain feature is a certain way and then pointing them to
the ZoP, they all pretty much told me after a certain time of thought that
"the decision made sense now."

Sorry to come across all harsh on the subject. Perhaps you know people
who are more meditative than I do, but I notice that you served up some
concrete advice elsewhere in the thread, so if the ZoP doesn't provide
any guidance to the questioner as it is, at least there's something
else to read through and grasp.

Paul
 
B

bonono

Steven said:
Strongly typed means that objects have a type. All objects in Python have
a type.

Strongly typed languages like Python forbid you from performing operations
on mismatched types, e.g. 1 + "1" does not work. In order to perform
operations on mismatched types, you must explicitly perform a conversion,
e.g. 1 + int("1").

Weakly typed languages do not prevent you performing operations on
mismatched types, e.g. something like 1 + "1" is allowed in languages like
Basic and Perl.

This much I know but it was not what we are talking about.
Untyped languages do not have any type information at all -- everything
is just bytes. The most obvious example is assembly language.

It should be noted that strong and weak typing is a matter of degree:
despite being mostly strongly typed, Python does do automatic coercion of
ints and floats, and although it is (arguably) weakly typed, Perl won't
allow you to treat scalars as arrays or vice versa.

Dynamic typing means that variables can be dynamically set to objects of
wildly different types. For Python, we would say that any name can be
bound to any object of any type.

Static typing is the opposite of dynamic typing. Once a variable or
name is defined as a certain type (either by a declaration, or
implicitly the first time it is used), it can only be assigned to values
of that same type.

These two articles may be helpful:

http://www.voidspace.org.uk/python/articles/duck_typing.shtml
http://www.artima.com/forums/flat.jsp?forum=106&thread=7590


A thoughtful defence of static typing is here:

http://www.xoltar.org/misc/static_typing_eckel.html

The fact that it is sub-titled "How Java/C++/C# Ruin Static Typing for the
Rest of Us" should give some idea what it is about.
And you would see in the xoltar link that Haskell is a language of
static typing. You cannot call a function with parameters with
incompatible types which would be checked at compile time.
 
P

Paul Boddie

Steven said:
Weakly typed languages do not prevent you performing operations on
mismatched types, e.g. something like 1 + "1" is allowed in languages like
Basic and Perl.

Actually, Perl and at least the version of BASIC that I previously used
are not weakly-typed languages either. The addition operator in Perl
uses coercion much as Python does/supports for some operand types, and
classic microcomputer BASICs generally suffixed variable names in order
to impose some kind of static typing. One classic example of a
weakly-typed language is BCPL, apparently, but hardly anyone has any
familiarity with it any more.

Paul
 
A

Aahz

Why would I want to use an attribute in Python, where I would use
getters and setters in Java? I know that encapsulation is actually just
a hack in Python (common, "hiding" an implementation detail by prefixing
it with the classname so you can't access it by its name anymore? Gimme
a break...), but is that a reason to only write white box classes? ^^

"Simple is better than complex."
 
S

Steven D'Aprano

Why would I want to use an attribute in Python, where I would use
getters and setters in Java?

Oh boy! I've just come out of a rather long thread about that very issue.
If you care enough to read a bunch of people arguing past each other,
check the thread "Another newbie question", especially the posts about the
so-called Law of Demeter.

But for the short summary: suppose I write a class:

class Parrot:
def __init__(self, x):
self._x = x
print "please use instance.getx() and setx()"
def getx(self):
return self._x
def setx(self, x):
self._x = x


What if I want to export methods of x other than get and set? Perhaps x
is a numeric value: now I have to export a whole series of arithmetic
operators: addx, subx, mulx, etc. And there may be other attributes I need
to export as well...

When I write getter and setter methods for an attribute, I make myself
responsible for maintaining everything about that attribute. I create
extra functions that need to be tested, debugged and documented. I expect
my class users to learn how to use my class, and every method is another
method they have to learn about. Writing getter and setter methods have
costs -- I pay some of those costs, and my users pay some of those costs.

Then I get fed up and write this class instead:

class NorwegianBlue:
def __init__(self, x):
self.x = x
print "please use public attribute instance.x"

NorwegianBlue is a much smaller class. That means less development time
for me, less test code to write, less documentation, less time needed for
my users to learn the API -- they already know what the API for x is,
because they supplied it. The costs to both class designer and class
user are much less, the flexibility is greater, and I finish writing the
class quicker.

Is there a disadvantage to NorwegianBlue? Yes -- I am now locked in to one
particular interface. I can't even do something as trivial as change the
name of attribute x. But then I can't change the name of Parrot.getx
either, not without breaking people's code.

But still, sometimes I know that I will want to -- or even that I might
want to -- radically change the implementation of my class. Perhaps x is a
list now, but later it will be a binary tree. How much effort will be
needed to fix the breakage if NorwegianBlue changes? If the expected
effort to fix the breakage is more than the effort to write setters and
getters, then it makes sense to write setters and getters.

But contrariwise, if it will take less effort to fix the problem than to
defend against it, then why defend against it? Getters and setters are
insurance against you changing the private interface. You wouldn't pay
$20,000 a year to protect a $20,000 car. You might not even pay $1,000 a
year.

So do your trade-offs, and make your decision.


I know that encapsulation is actually just
a hack in Python (common, "hiding" an implementation detail by prefixing
it with the classname so you can't access it by its name anymore? Gimme
a break...),

I'll give you a much better break in C++:

#def private = public

So much for encapsulation, hey?

Java makes it harder, but not that much more:

http://www.informit.com/articles/article.asp?p=174217&seqNum=3&rl=1


but is that a reason to only write white box classes? ^^

Why would you want to write black box classes?

Python is not a bondage and discipline language. Remember kids, whips and
chains are only for mummies and daddies who love each other very much.

Python includes tools for preventing people *accidentally* shooting
themselves in the foot. But the philosophy of the language is to allow,
even to encourage, people to tinker under the hood. If that means that
somebody *deliberately* shoots themselves in the foot, well, that's the
price you pay for freedom: some people make stupid decisions.

I look at it this way: as the class designer, I have ZERO idea what
attributes and methods of my class will be just the perfect thing to solve
somebody's problem, so it is rude of me to lock them up as private --
especially since they *will* find a way to hack my class and access my
private attributes and methods anyway. All I'm doing is making their life
miserable by making them go out and buy books like "Hacking Private
Attributes In Java" or something.

But as a class designer, I know perfectly well which of my class members
are free for anyone to touch (instance.public), which should be considered
private by convention (instance._private) and which need a great big sign
on the door saying "Enter At Own Risk! Don't touch this unless you know
what you are doing!!!" (instance.__mangled).
 
A

Alex Martelli

Matthias Kaeppler said:
I stumbled over this paragraph in "Python is not Java", can anyone
elaborate on it:

"In Java, you have to use getters and setters because using public
fields gives you no opportunity to go back and change your mind later to
using getters and setters. So in Java, you might as well get the chore
out of the way up front. In Python, this is silly, because you can start
with a normal attribute and change your mind at any time, without
affecting any clients of the class. So, don't write getters and setters."

Why would I want to use an attribute in Python, where I would use
getters and setters in Java? I know that encapsulation is actually just
a hack in Python (common, "hiding" an implementation detail by prefixing
it with the classname so you can't access it by its name anymore? Gimme
a break...), but is that a reason to only write white box classes? ^^

Consider the difference between asking a user of your class to write:

a.setFoo(1 + a.getFoo())

versus allowing him or her to write:

a.foo += 1

I hope you can see how much more elegant and handy the second
alternative is. The point is, in Python, you can easily make the
semantics of the second alternative identical to those of the first: all
you need to do is, within a's class, add ONE line:
foo = property(getFoo, setFoo)
and every access of a.foo will become a call to a.getFoo(), every
setting of a.foo=bar a call to a.setFoo(bar).

The ability to do this IF AND WHEN getFoo and setFoo really need to
exist (i.e., they do something meaningful, not just boilerplate setting
and getting of plain attributes) empowers you to always allow the users
of your class to access attributes -- you will change an attribute to a
property only in future versions of your class that do need some
meaningful action upon the getting or setting or that attribute.


Alex
 
A

Alex Martelli

Matthias Kaeppler said:
I'm so used to statically typed languages that the shift is very
confusing. Looks as if it isn't as easy to learn Python afterall, for
the mere reason of unlearning rules which don't apply in the world of
Python anymore (which seem to be quite a lot!).

If you start your reasoning with Java for most aspects (and with C++ for
just a few) it may get easier -- for example, garbage collection, the
semantics of assignment and argument passing, as well as the
immutability of strings, are very close in Java and Python, and they're
such a "bedrock layer" parts of the languages that IMHO they're more
likely to be stumbling blocks if you "think C++" rather than "think
Java", while learning Python.

If you can imagine a Java program where everything is declared to be
Object, all method calls have an implicit cast to "some interface
supplying this method", and all class declarations implicitly add many
"implements IMethodFoo" for automatic interfaces supplying each of their
methods foo, you're a good part of the way;-). Such a style would be
unusual (to say the least) in Java, but it's clearly POSSIBLE... it's
actually not that unusual in Objective C (except you say ID rather than
Object, and implicit interfaces ARE sort of there) -- the main caveat i
would give to an Objective C person learning Python (besides the garbage
collection issues) is "in Python, you cannot call arbitrary methods on
None and expect them to be innocuous noops" (like in Java or C++,
calling anything on None, aka a NULL pointer aka null, is a runtime
error in Python too; the language without this restriction in this case
is Objective C!-).


Alex
 
A

Alex Martelli

Steven D'Aprano said:
Oh boy! I've just come out of a rather long thread about that very issue.
If you care enough to read a bunch of people arguing past each other,
check the thread "Another newbie question", especially the posts about the
so-called Law of Demeter.

But for the short summary: suppose I write a class:

class Parrot:
def __init__(self, x):
self._x = x
print "please use instance.getx() and setx()"
def getx(self):
return self._x
def setx(self, x):
self._x = x

What if I want to export methods of x other than get and set? Perhaps x
is a numeric value: now I have to export a whole series of arithmetic
operators: addx, subx, mulx, etc. And there may be other attributes I need
to export as well...

Sorry, I don't see this.

a.setx( b.getx() + c.getx() - d.getx() )

will work just as well as (in a better-designed class) would

a.x = b.x + c.x - d.x

It's just that the former style you force syntactic cruft and overhead
which you may save in the latter. "Exporting a series of operators",
which was an issue in the LOD thread, is not one here: once you have
setter and getter, by whatever syntax, it's not germane.


Alex
 
P

Paul Boddie

Steven said:
I look at it this way: as the class designer, I have ZERO idea what
attributes and methods of my class will be just the perfect thing to solve
somebody's problem, so it is rude of me to lock them up as private --
especially since they *will* find a way to hack my class and access my
private attributes and methods anyway.

Actually, one good aspect of "attribute privacy" is the lowered risk of
instance attribute name clashes. When subclassing from some partly
abstract class in order to specialise its behaviour
(sgmllib.SGMLParser, for example), one has to make sure that one
doesn't reuse some instance attribute name by accident - doing so could
potentially cause behavioural issues in the superclass's mechanisms.
That said, Python does at least let you look at which attributes are
already defined in an instance due to the lack of privacy, so it does
offer some kind of solution to that problem. Moreover, Python also lets
you define double-underscore attribute names which behave give instance
attributes privacy in all respects, being invisible to users of the
instances concerned, accessible only within methods belonging to the
defining class, and safely overloadable in subclasses without incurring
conflicts.

Generally, I'm in agreement with you, though: private, protected and
final are all things which provide easy distractions from more serious
and demanding issues. They can quite often prove to be "the bikeshed"
in system design and implementation.

Paul
 
X

Xavier Morel

Matthias said:
Why would I want to use an attribute in Python, where I would use
getters and setters in Java? I know that encapsulation is actually just
a hack in Python (common, "hiding" an implementation detail by prefixing
it with the classname so you can't access it by its name anymore? Gimme
a break...), but is that a reason to only write white box classes? ^^

- Matthias

If you've ever written non-trivial code in Java, you can't deny that
most of the classes are littered by pages and pages of

--
protected FooClass foo;
FooClass getFoo() {
return foo;
}
void setFoo(FooClass foo) {
this.foo = foo;
}
--

This is more or less equivalent to a simple
--
public FooClass foo;
--
but allows you to change the implementation details of the class without
having to bork your whole interface later.

Now, you have no reason to do this in python, you can use a regular
"real" attribute, and if you need to change the implementation details,
you can remove the real attribute and replace it with a virtual
attribute through properties without changing the class' interface.
-- def __init__(self):
self.foo = 5

def __init__(self):
self.boo = 2
self.far = 3
def _getfoo(self):
return self.boo + self.far
foo = property(_getfoo)

5
--
And this is completely transparent for anyone using the Bar class, they
don't give a damn about what happens inside.

You can also use it to add checks on the allowed values, for example
-- def __init__(self):
self._foo = 5
def _getfoo(self):
return self._foo
def _setfoo(self,foo):
if not 0 <= foo <= 10:
raise ValueError("foo's value must be between 0 and 10 (included)")
self._foo = foo
foo = property(_getfoo, _setfoo)


Traceback (most recent call last):
File "<pyshell#42>", line 1, in -toplevel-
bar.foo = 20
File "<pyshell#37>", line 8, in _setfoo
raise ValueError("foo's value must be between 0 and 10 (included)")
ValueError: foo's value must be between 0 and 10 (included)
 
A

Alex Martelli

Paul Boddie said:
offer some kind of solution to that problem. Moreover, Python also lets
you define double-underscore attribute names which behave give instance
attributes privacy in all respects, being invisible to users of the
instances concerned, accessible only within methods belonging to the
defining class, and safely overloadable in subclasses without incurring
conflicts.

Unfortunately, that depends on the subclasses' naming:

in module bah.py:
class Foo(object) ...

in module zot.py:
import bah
class Foo(bah.Foo) ...

and alas, the "privacy in all respects" breaks down. Now, the idea of
"class Foo(bah.Foo)" may look silly with such artificial names, but it
IS somewhat likely to happen in the real world when the classes' names
are meaningful. I guess pychecker or the like might check for this and
issue a warning if needed... but I do wish we had better ways to prevent
accidental naming conflicts (not that I can easily think of any).


Alex
 

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,779
Messages
2,569,606
Members
45,239
Latest member
Alex Young

Latest Threads

Top