Does Python really follow its philosophy of "Readability counts"?

Luis Zarrabeitia kyrie at uh.cu
Tue Jan 20 01:10:09 EST 2009


Quoting Paul Rubin <"http://phr.cx"@NOSPAM.invalid>:

> Luis Zarrabeitia <kyrie at uh.cu> writes:
> > > Luis Zarrabeitia <kyrie at uh.cu> writes:
> > class ImmutableType(type): ...
> 
> Thanks for posting this, I haven't replied because I bookmarked the
> post for later study, but that was several days ago, so I just wanted
> to say that I'm still looking at it.  It's an area of Python that I've
> somewhat purposely stayed away from.

Hehe. Keep away - once you get dive into it, it'll be hard go get out. It's
really addictive :D

> > I use them, a lot, specially when writing decorators... Many times I
> > just want to 'mark' the decorated functions so I can inspect those
> > marks later.  I'd rather have a semi-private namespace for each pair
> > ([group of]calling function[s], object),
> 
> Right, thus the usefulness of a static tool that can find all those
> places where extra attributes are created.  Basically I can see three
> approaches to this question:

Btw, this thread inspired me to do another proof-of-concept stuff that I may be
using more seriously. Too bad I don't have anywhere to upload it right now...
Anyway, it looks like this: suppose I need to attach some attributes to the
object "x", but I don't want to do x.attr 

import namespaces # my module
class my_private_namespace(namespaces.Namespace): pass

def this_function_stores_the_attr(x):
    ns = my_private_namespace(x)
    ns.attr = 5 # I'm creating an attribute on the fly
                # but at least I'm not touching x.

def this_function_reads_the_attr(x):
    ns = my_private_namespace(x)
    print ns.attr
 
> 1) Program in a style where you create and modify attributes all over
> the place willy-nilly, with no controls whatsoever, either automatic
> or stylistic.  From experience (it is a common style in Javascript) I
> can say this is a big mess and I think no experienced Python
> programmers recommend it.

Indeed. Though I don't consider myself 'experienced', I certainly don't
recommend it.

> 2) Have mandatory encapsulation like Java's, no way to escape.  Bruno
> and yourself vehemently object to this, Russ P seems to want it, my
> view is that it's not in the Python spirit, so I can accept the
> argument that those who really want this are better off choosing
> another language.

Btw, I noticed a funny side effect of my 'namespaces' module (needs a LOT of
work, this was the first time I touched weak references. I should find some
place to upload the module):

=====
import namespaces

class public(namespaces.Namespace): pass
class private(namespaces.Namespace): # the initializer could check if
    pass  # its called from outside the class. Performance penalty.

class A(object):
    """dummy class A, with one 'private' attribute x, and one
       normal 'attribute' x."""
    def __init__(self):
        self.x = id(self) # normal attr
        pub = public(self)
        pub.f = self.f
        priv = private(self)
        priv.x = "this is the private"
    def f(self):
        print "normal x: ", self.x
        priv = private(self)
        print "private x: ", priv.x


>>> a = A()
>>> dir(a)
['__class__', <normal __attrs__>,  'f', 'x']
>> a.f()
normal x: 147956684
private x: this is the private

>>> p = public(a)
>>> dir(p) # Look, there is no 'x' attribute!
['__class__', <normal __attrs__>,  'f']
>>> p.f() # but f still works!
normal x: 147956684
private x: this is the private
======

Of course, without checks in 'private', one could say priv = private(a); priv.x,
but the most interesting part, for me at least, was the ability to define a
'public' interface for a, that contains no attribute directly referencing the
object 'a' with its internals - only the names explicitly declared on the
'interface'. Indirectly... well, p.f.im_self will return back the 'a' object.

Bah, I'm ranting now. I just got carried away with my little experiment. Sorry.
Ah, and yes, metaclasses are dangerously addictive :D

> 3) Program in a style where creating new attributes outside the
> initializer is normally avoided by convention, but it is possible and
> sometimes desirable to break the convention.  This seems reasonable
> and Pythonic to me.  But, any place I break the convention, I should
> be able to say "I broke the convention here for such and such a
> reason".  And (this seems to be a bone of contention) IMO, it would be
> good to be able to audit a large codebase with a tool like Pylint, to
> automatically find all the places where such breakages occur.  Right
> now there seems to be no way to do that reliably, short of using
> something like your special metaclass instead of the standard one for
> just about everything.

Well, I agree. Each and every one of those convention-breakages should be well
documented, and pylint should be able to pick them up. Pylint does that already
(see example below), I don't know if it lets you "document" it explicitly. I
think Eclipse catches some of that stuff. My metaclass... shouldn't be used for
anything serious :D.

=== breaklint.py === (no, it didn't break pylint)
class A(object):
    def __init__(self):
        self.a = 5
    def f(self):
        self.b = 7  # creating outside the init
    def g(self):
        print self.b # only if f runs first
        print self.c # and this one doesn't exist

a = A()
a.c = 8
a.f()
a.g()
a.d = "new var" # outside the class definition
==================
$ pylint breaklint.py
[...]
W: 13: Attribute 'c' defined outside __init__
W:  7:A.f: Attribute 'b' defined outside __init__
W: 16: Attribute 'd' defined outside __init__
[...]
Your code has been rated at -1.54/10 

=====

> Your example about decorators (used on classes that don't expect to be
> wrapped by them) is a good one but narrow enough that there could be a
> special arrangement for it in a hypothetical auditing tool.

Indeed. I'll take a look at pylint's config file.

-- 
Luis Zarrabeitia
Facultad de Matemática y Computación, UH
http://profesores.matcom.uh.cu/~kyrie



More information about the Python-list mailing list