[OPINION] - does language really matter if they all do the samething?
Dietrich Epp
dietrich at zdome.net
Sat Jan 24 00:44:15 EST 2004
Please understand, before you read this, that I think Python is the
most elegant language that looks and feels natural. My point is that
there are plenty of programming techniques which aren't natural in
Python, but are still useful. I feel Python's strength is that it is
flexible enough that most patterns that many programmers use day-to-day
map very easily and naturally into readable Python code. The problem
is that often when you use a pattern that doesn't fit the language you
use, it will be difficult to code, harder to maintain, and behave
unexpectedly meanwhile.
The rest of the post is dedicated to two examples of patterns which
don't quite fit into Python, but are used well in some projects written
in other languages.
On Jan 23, 2004, at 5:04 PM, Paul Prescod wrote:
> Dietrich Epp wrote:
>> ...
>> <abestos>
>> Python seems to take the middle ground. It's build from a procedural
>> standpoint, in an environment where most things are objects and some
>> are functions. It's not what I'd choose as an example of an object
>> oriented language. There are no messages, only function calls, so
>> you can't capture them.
>
> That's certainly not true. Take a look at how the XML-RPC libraries
> work. They capture "messages" sent from the program to the proxy
> object, reify them as XML "messages" sent across the network and then
> turned back into function calls applied to objects.
Well, I don't mean that you absolutely can't capture a message, but
it's just not something that's natural in Python the way it is in
Objective C. In Objective C, I can create an object which doesn't
respond to some message "foo". However, if I send "foo" to the object,
it gets a chance to respond. In Python, there is no concept of
messages. You would have to override the function that gets an
object's attribute and create a function for it to return. You can
capture function calls, but capturing method calls is a two-step
process in Python which requires a little bit of magic to implement.
The problem is that I didn't define message. In python, sending the
"bar" message to the "foo" object is written as "foo.bar()". But it
isn't the same as sending a message, because in Python, it's two steps:
get the "bar" property of "foo", and call it.
The example of this is at the end of this post.
>> The plus side is that methods behave exactly the same as
>> functions, this makes the language simple and the two ideas
>> interchangeable. You can set an object's method to a function. The
>> minus side is that the paradigm of sending a message "to" an object
>> doesn't exist, and anyone coming from Smalltalk or even Objective-C
>> might miss that and feel constrained (I did). But if Python were
>> really object-oriented like that, then it wouldn't have Python's
>> simplicity any more.
>> </abestos>
>
> I would appreciate an example of something you would do by capturing a
> message that cannot be done in Python.
>
> I rather wonder if you've illustrated the downside of flitting from
> language to langauge. You may think you're using a language to
> capacity without actually doing so. I could be wrong but perhaps even
> the tasks you turn to Lisp or Perl for may have easy equivalents in
> Python.
If I wanted to make this personal, I would accuse you of choosing your
patterns to fit the language rather than vice versa. Or perhaps no
useful pattern exists which is difficult in Python and easy in a
different language? I love Python too, but it's only one of the tools
in my box.
Of course, you haven't seen my code, nor I yours. My most recent Perl
script was about twenty lines of nothing but regular expressions and a
couple dictionary operations. That would have been even less readable
and writable in Python. My Lisp project related to an RPG and random
generation of items. It had lots of code like the following:
(defun random-sword-magic-power (quality)
(choose-random-assoc
quality (poor medium good)
((5 0 0) (glows-in-the-dark))
((3 3 0) (magically-silent))
((1 5 1) (elemental-power (select-random '(earth water air fire))))
((0 2 4) (magical-keen-edge))
((0 0 2) (append (random-sword-magic-power 'medium)
(random-sword-magic-power 'medium)))))
The numbers on the left are probabilities relative to the other
probabilities in the same column. So, when generating a random 'poor'
magic quality of a sword, you have a 5/9 chance of getting 'glow in the
dark'. For a 'good' quality, you have a 2/7 chance of getting two
'medium' qualities instead. It is difficult for me to imagine how one
would go about making this function more concise, to me it looks
downright minimal. The only thing I could see making it smaller would
be removing the parentheses, but they would have to be replaced by
something else such as commas or tabs.
I'm not trying to say that all applications are like my application,
and I'm not trying to say that my application can't be written in
Python. I'm just saying that using macros, a paradigm that Python
doesn't even come close to supporting, makes reading and writing
functions like the above a lot easier. You don't even need to know
that 'choose-random-assoc' is a macro, you just need to know how to use
it. Heck, defun is a macro in Clisp.
I challenge anyone to come up with a better way to express the above
function in Python. If I think it's better, I'll write "PYTHON RULZ"
on my forehead and post a photo on the web.
On Jan 23, 2004, at 5:35 PM, Brian Quinlan wrote:
> Dietrich wrote:
>> Python seems to take the middle ground. It's build from a procedural
>> standpoint, in an environment where most things are objects and some
>> are functions.
>
> I'd be interested to know why you don't think that functions are
> objects. I
> would argue that they are because they have attributes, methods, and
> are
> bound to variables the same way as other Python objects.
I agree that functions are objects in Python. However, not everything
in Python is an object. For example, names are not objects. In the
above example, "foo.bar()", neither "foo" nor "bar" are objects, but
they are references to objects. In some languages, such as C and Lisp,
references to objects can be directly manipulated (Lisp has symbols, C
has pointers).
>> It's not what I'd choose as an example of an object
>> oriented language. There are no messages, only function calls, so you
>> can't capture them.
>
> You certainly can capture them. Could you demonstrate the Objective-C
> or
> Smalltalk syntax for doing so? Maybe it is a lot easier in those
> languages.
In Objective C:
- (void)forwardInvocation:(NSInvocation *)invocation
{
if ([someOtherObject respondsToSelector:[invocation selector]])
[invocation invokeWithTarget:someOtherObject];
else
[self doesNotRecognizeSelector:[invocation selector]];
}
In Python:
def __getattr__(self, name):
try:
return super(this_class, self).__getattr__(name)
except AttributeError:
try:
method = super(this_class,
self).some_other_object.__getattr(name)
if iscallable(method):
return method
except:
pass
raise
In Python you have to be careful because this won't normally capture
just method calls, but all attribute accesses. In a typical
Objective-C application you'll probably see a lot of places where the
program sends a message to an object only if the object responds to
that message. Let's compare.
In Objective C:
- (void)resetToDefaultSettings
{
if ([delegate respondsToSelector:@selector(defaultSettingsFor:)])
[self setSettings:[delegate defaultSettingsFor:self]];
else
[self setSettings:someFallbackValue];
}
In Python, first try:
def reset_settings(self):
try:
self.set_settings(self.delegate.default_settings(self))
except AttributeError:
self.set_settings(some_fallback_value)
But wait! What if there is an error in
self.delegate.default_settings() which raises an AttributeError? It
might confuse the hell out of a programmer who might go looking for the
bug in reset_settings(), because it's using the fallback value for some
odd reason. So we should reimplement it.
Python, second try:
def reset_settings(self):
method = None
try:
method = self.delegate.default_settings
except AttributeError:
pass
if method:
self.set_settings(method(self))
else:
self.set_settings(some_fallback_value)
If you think this example is contrived, maybe you haven't worked with
the paradigms used in Objective-C very much. The example above was
used dozens of times in a class that I wrote in Objective-C which draws
a grid to the screen, for example, when it asks itself or its delegate
what color the grid should be, or the maximum scale allowed. The
practice is also used heavily by Apple's Cocoa libraries, which are my
favorite UI libraries of all time to program with.
More information about the Python-list
mailing list