unpythonic use of property()?

J

J Kenneth King

Consider:

code:
------------------------------------------------------------------------

class MyInterface(object):

def __get_id(self):
return self.__id

id = property(fget=__get_id)

def __init__(self, id, foo):
self.__id = id
self.foo = foo


class MyInterface2(object):

def __init__(self, id, foo):
self._id = id
self.foo = foo

@property
def id(self):
return self._id

------------------------------------------------------------------------

The situation is that an API to an external resource must enforce
consistency with the resource's expectations of data (ie: ID's must be
immutable).

The Python docs clearly show the use of the property() decorator in both
the example classes shown here. Both ensure that an assignment to the
class objects' 'id' attribute will raise an AttributeError
exception. Both will satisfy the requirements.

I assume we all know what the implementation differences are between the
two examples here. What I am curious about is what use case would the
MyInterface example have? My guess is that it's added protection for
attribute names on objects generated from factories or generators. I'd
like a more concrete explanation from someone who has encountered a
scenario where it was required.

I was recently informed that it was 'unpythonic' and have since been a
little confused by the term. I've heard it bandied about before but
never paid much attention. What is 'unpythonic'? What about this example
is unpythonic?
 
C

Carl Banks

Consider:

code:
------------------------------------------------------------------------

class MyInterface(object):

    def __get_id(self):
        return self.__id

    id = property(fget=__get_id)

    def __init__(self, id, foo):
        self.__id = id
        self.foo = foo

class MyInterface2(object):

    def __init__(self, id, foo):
        self._id = id
        self.foo = foo

    @property
    def id(self):
        return self._id

------------------------------------------------------------------------

The situation is that an API to an external resource must enforce
consistency with the resource's expectations of data (ie: ID's must be
immutable).

The Python docs clearly show the use of the property() decorator in both
the example classes shown here. Both ensure that an assignment to the
class objects' 'id' attribute will raise an AttributeError
exception. Both will satisfy the requirements.

I assume we all know what the implementation differences are between the
two examples here. What I am curious about is what use case would the
MyInterface example have? My guess is that it's added protection for
attribute names on objects generated from factories or generators. I'd
like a more concrete explanation from someone who has encountered a
scenario where it was required.

The MyInterface2 version is preferred because it avoids creating an
extra symbol in the class's namespace. That's pretty much it.

The MyInterface example was how you had to do it before there were
decorators. It still works, and the world won't end if you do it that
way. There is not really much practical difference between the two
versions to someone using the class.

Also, the MyInterface version stores its data in a name-mangled
attribute (two underscores), but you can also use a name-mangled
attribute for the MyInterface2 version if you want.

Neither version is foolproof.

I was recently informed that it was 'unpythonic' and have since been a
little confused by the term. I've heard it bandied about before but
never paid much attention. What is 'unpythonic'? What about this example
is unpythonic?

There are different reasons someone might have said it.

It's probably just for the reason I mentioned. Python likes to have
an quasi-official way to do basic things such as define properties.
("There should be one--and preferrably only one--obvious way to do
it.") That current quasi-official way is to use property is as a
decorator, for the reason I gave: it avoids namespace pollution.

Some people think attribute name-mangling is unpythonic. It's true
that people sometimes mistakenly treat it a solid information hiding
mechanism, but I wouldn't call its usage unpythonic when used as
intended: as a way to avoid name-collisions. If you think it's
worthwhile to protect an attribute from being overwritten, you might
as well guard against accidental conflict with the underlying name.

Finally, some people think read-only attributes are unpythonic. I
think that's ridiculous, although in general I'd advise against making
attributes read-only willy-nilly. But there's a time and place for
it.

Last thing I'll advise is don't get too hung up on terms like
"Pythonic". Treat such labels are more of a red flag and get people
who throw out the term to explain why.


Carl Banks
 
C

Carl Banks

Here you are assuming that a user of your class could not possibly have a
valid reason for getting to the underlying variable.  Don't make those
decisions for someone else, in Python, "we are all adults here."

They can use the demangled name of the internal variable if they want
access to it.

Generally, properties are for doing some form of calculation, not
for making things read-only.

That might be how properties are "generally" used, but if for some
reason I wanted a read-only attribute, that's how I'd do it.


[snip strawman stuff]
 It is not
your job to protect those users who do not use your code properly from
themselves; that way lies madness.

I'm sorry, but the universe is not as simple as you are making it out
to be. Blanket statements like the one you just gave here are not
something that should ever be blindly adhered to.

If, in my judgment, users would be prone to overwrite one of my
attributes, and if I designed the system to rely on that attribute,
and if the results of changing it are bad enough, then by golly I'm
going to make the attribute harder than usual to modify. And yes,
that is my job.

Users who want to change it anyway can curse me and then go demangle
the name themselves.


Carl Banks
 
J

J Kenneth King

Carl Banks said:
Here you are assuming that a user of your class could not possibly have a
valid reason for getting to the underlying variable.  Don't make those
decisions for someone else, in Python, "we are all adults here."

They can use the demangled name of the internal variable if they want
access to it.

Generally, properties are for doing some form of calculation, not
for making things read-only.

That might be how properties are "generally" used, but if for some
reason I wanted a read-only attribute, that's how I'd do it.


[snip strawman stuff]
 It is not
your job to protect those users who do not use your code properly from
themselves; that way lies madness.

I'm sorry, but the universe is not as simple as you are making it out
to be. Blanket statements like the one you just gave here are not
something that should ever be blindly adhered to.

If, in my judgment, users would be prone to overwrite one of my
attributes, and if I designed the system to rely on that attribute,
and if the results of changing it are bad enough, then by golly I'm
going to make the attribute harder than usual to modify. And yes,
that is my job.

Users who want to change it anyway can curse me and then go demangle
the name themselves.


Carl Banks

Thanks for all the replies --

While greatly simplified, the reason for desiring read-only attributes
in this case is that the interface is to a remote server whose policy
relies on data objects represented by this class to have a few values
which never change or are managed by the server and not the end
user. Changing the ID value would break things on the server, so I
wanted to write the interface class to respect those conventions.

I'm well aware that if a developer really wanted to, they could get
around it no matter what I did, but I figure that if I at least make
it really difficult it will be obvious that they're really going into
dangerous territory.

Further (and this might just be a tad paranoid), user interface code
which might use this API might be dangerous. It's one thing for a
developer to break the rules when they need to, but a user shouldn't
be able to. By enforcing read-only on the API it ensure (at least in
my world view) that a developer writing a user interface against it
won't have to code defensively against malicious input.

However, since the difference between the two is simply attribute
name-mangling it's practically a pedantic issue. I guess there might
be some hyper-specific scenario where MyInterface would still be
useful, but this one might not be it.

Again, thanks. :)
 
M

Michele Simionato

Consider:

code:
------------------------------------------------------------------------

class MyInterface(object):

    def __get_id(self):
        return self.__id

    id = property(fget=__get_id)

    def __init__(self, id, foo):
        self.__id = id
        self.foo = foo

I use the double underscore/name mangling mechanism only if I am sure
that the
__xxx attribute will be overridden in subclasses, and overriding
without
protection will cause issues. Otherwise I don't bother, I use a single
underscore.
In my mind it is obvious that a private attribute _xxx attribute
should not
be overridden in subclasses without care. The double underscore
instead says "you can override me freely, don't worry" and I reserve
it
for attributes which *should* be overridden.
 
L

Luis Zarrabeitia

Changing the ID value would break things on the server, so I
wanted to write the interface class to respect those conventions.

Then, take this opportunity fix the server and prevent it from breaking once
you change the ID, because:
I'm well aware that if a developer really wanted to, they could get
around it no matter what I did, but I figure that if I at least make
it really difficult it will be obvious that they're really going into
dangerous territory.

you will have to do it anyway.

I think it's great, for you, that the language you are using makes it so
extremely easy to bypass almost any access restriction that you may put in
the data sent to your clients. That means that you won't be overconfident,
that you are forced to make sound decisions from the beginning, and that you
won't be trusting that your clients will be well behaved (even with very
strong enforcement of data hiding, a malicious client could forge the
connection).

Then, when the server is safe from user data, by all means go and make sure
that the clients (and even the server!) will complain as loudly as possible
if they want to break encapsulation.
 
J

J Kenneth King

Luis Zarrabeitia said:
Then, take this opportunity fix the server and prevent it from breaking once
you change the ID, because:

Unfortunately it's not my server to fix. I can suggest a patch, but
that's it.
you will have to do it anyway.

I think it's great, for you, that the language you are using makes it so
extremely easy to bypass almost any access restriction that you may put in
the data sent to your clients. That means that you won't be overconfident,
that you are forced to make sound decisions from the beginning, and that you
won't be trusting that your clients will be well behaved (even with very
strong enforcement of data hiding, a malicious client could forge the
connection).

Then, when the server is safe from user data, by all means go and make sure
that the clients (and even the server!) will complain as loudly as possible
if they want to break encapsulation.

Well, really all I'm doing is writing a light framework around xmlrpclib.

The design allows me to keep this kind of exception highly localized,
so its not really a huge practical issue.

The problem I had was that I found two approaches to the same problem
that appeared to be pedantic in difference.

However, you are right of course -- logically the server should
protect against this vulnerability itself instead of the
implementation using my framework.
 
L

Luis Zarrabeitia

Unfortunately it's not my server to fix. I can suggest a patch, but
that's it.

Yes, that's unfortunate.
Btw, when I re-read my phrase by itself, it seemed hostile... My apologies.
I'm still not very good at expressing my thoughts in english.

Then, I guess, you have little choice. Mangle the name, hope that the server
will get fixed.
 
J

J Kenneth King

Luis Zarrabeitia said:
Yes, that's unfortunate.
Btw, when I re-read my phrase by itself, it seemed hostile... My apologies.
I'm still not very good at expressing my thoughts in english.

It's okay.

You have great English. ;)
 

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

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,740
Latest member
JudsonFrie

Latest Threads

Top