473,287 Members | 3,319 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,287 software developers and data experts.

design question: no new attributes

I have a class whose instances should only receive attribute
assignments for attributes that were created at inititialization.
If slots are not appropriate, what is the Pythonic design for this?

Thanks,
Alan Isaac
Feb 26 '07 #1
28 1435
Alan Isaac wrote:
I have a class whose instances should only receive attribute
assignments for attributes that were created at inititialization.
If slots are not appropriate, what is the Pythonic design for this?

Thanks,
Alan Isaac

My understanding of "Pythonic design" is not to worry about it.
If users want to set attributes that won't accomplish anything
productive (within your class) let them.

-Larry
Feb 26 '07 #2
"Alan Isaac" <ai****@american.eduwrites:
I have a class whose instances should only receive attribute
assignments for attributes that were created at inititialization.
If slots are not appropriate, what is the Pythonic design for this?
The Pythonic design is: don't expect to have such control over users
of your code. (I encountered this in trying to design an Enum class,
with enumeration members as attributes.)

Document, in the class doc string and anywhere else you feel
appropriate, the fact that attributes shouldn't be added, and let your
users deal with their own bugs if they ignore that.

--
\ "No one ever went broke underestimating the taste of the |
`\ American public." -- Henry L. Mencken |
_o__) |
Ben Finney

Feb 27 '07 #3
"Ben Finney" <bi****************@benfinney.id.auwrote in message
news:ma***************************************@pyt hon.org...
The Pythonic design is: don't expect to have such control over users
of your code.
I know this is a popular response,
but the fact of the matter remains that
it can be helpful to know when someone
tries to set a value for a nonexisting attribute.
This is especially true if there have been any
interface changes.

So my question remains:
how best to trap any such attempt
subsequent to object initialization?
(I am willing to have properties for
all data attributes, if that helps.)

Thank you,
Alan Isaac
Feb 27 '07 #4
On Tue, 27 Feb 2007 06:40:29 +0000, Alan Isaac wrote:
"Ben Finney" <bi****************@benfinney.id.auwrote in message
news:ma***************************************@pyt hon.org...
>The Pythonic design is: don't expect to have such control over users
of your code.

I know this is a popular response,
It's popular for good reason.

but the fact of the matter remains that
it can be helpful to know when someone
tries to set a value for a nonexisting attribute.
I'm afraid your understanding of the word "fact" is different from my
understanding of the word "fact".

But be that as it may, if you wish to waste^H^H^H^H^H spend time
trying to prevent people from adding attributes to their instances,
Just Because, you can do something like this:

class Difficult(object):
def __setattr__(self, name, value):
if self.__dict__.has_key(name):
print "'%s' exists as an instance attribute" % name
self.__dict__[name] = value
elif self.__class__.__dict__.has_key(name):
print "'%s' exists as a class attribute" % name
self.__class__.__dict__[name] = value
else:
print "Can't create new attributes, 'cos I said so!"
There ought to be a name for that anti-pattern of molly-coddling,
bondage-and-domination philosophy of "you're only allowed to use my class
the way I want you to use it".

(Excuse my cynicism, for all I know you've got a really good reason for
wanting to do this, perhaps as part of a cold-fusion machine.)

--
Steven D'Aprano

Feb 27 '07 #5
On 27 Feb, 06:40, "Alan Isaac" <ais...@american.eduwrote:
So my question remains:
how best to trap any such attempt
subsequent to object initialization?
(I am willing to have properties for
all data attributes, if that helps.)
You can define the __setattr__ method in your class as

def __setattr__(self, attr, val):
if hasattr(self, attr):
self.__dict__[attr] = val
else:
# Tell the user off

but of course the user can set attributes through
instance.__dict__['attrname'] = val

So to really do it you would need to design your own metaclass

--
Arnaud
Feb 27 '07 #6
"Alan Isaac" <ai****@american.eduwrites:
"Ben Finney" <bi****************@benfinney.id.auwrote:
The Pythonic design is: don't expect to have such control over
users of your code.

I know this is a popular response, but the fact of the matter
remains that it can be helpful to know when someone tries to set a
value for a nonexisting attribute.
That's quite a different request from your original request asking the
Pythonic design for *preventing* setting of new attributes.
This is especially true if there have been any interface changes.
If the interface has changed, then you don't need to trap *every*
setting of an attribute; only the names that you know have changed.

===== foo_v1.py =====

class Foo(object):
def bar(self):
return 17

spam = 42
=====

===== foo_v2.py =====

import logging

class Foo(object):
def baz(self):
return 13

eggs = 69

def bar(self):
""" Obsolete interface for Foo.baz() """
logging.warn("Deprecated Foo.bar() call, please use Foo.baz()")
return self.baz()

def _get_spam(self):
""" Obsolete interface for Foo.eggs """
logging.warn("Deprecated Foo.spam reference, please use Foo.eggs")
return self.eggs
def _set_spam(self, value):
""" Obsolete interface for Foo.eggs """
logging.warn("Deprecated Foo.spam reference, please use Foo.eggs")
self.eggs = value
spam = property(_get_spam, _set_spam)
=====

After a reasonable grace period, you can drop these compatibility
interfaces in a future version.

--
\ "For every complex problem, there is a solution that is simple, |
`\ neat, and wrong." -- Henry L. Mencken |
_o__) |
Ben Finney

Feb 27 '07 #7
"Steven D'Aprano" <st***@REMOVEME.cybersource.com.auwrote in message
news:pa****************************@REMOVEME.cyber source.com.au...
class Difficult(object):
def __setattr__(self, name, value):
if self.__dict__.has_key(name):
print "'%s' exists as an instance attribute" % name
self.__dict__[name] = value
elif self.__class__.__dict__.has_key(name):
print "'%s' exists as a class attribute" % name
self.__class__.__dict__[name] = value
else:
print "Can't create new attributes, 'cos I said so!"

But this prevents setting attributes during initialization,
so it does not meet the spec.
Cheers,
Alan
Feb 27 '07 #8
"Arnaud Delobelle" <ar*****@googlemail.comwrote in message
news:11**********************@h3g2000cwc.googlegro ups.com...
def __setattr__(self, attr, val):
if hasattr(self, attr):
self.__dict__[attr] = val
else:
# Tell the user off
But then you cannot even set attributes during initialization, right?
I want that restriction only after an object is initialized.
(I realize that I could condition on an attribute set during initialization,
but I am asking for the most elegant way to achieve this.)

Alan
Feb 27 '07 #9
On 2/27/07, Alan Isaac <ai****@american.eduwrote:
"Arnaud Delobelle" <ar*****@googlemail.comwrote in message
news:11**********************@h3g2000cwc.googlegro ups.com...
def __setattr__(self, attr, val):
if hasattr(self, attr):
self.__dict__[attr] = val
else:
# Tell the user off

But then you cannot even set attributes during initialization, right?
I want that restriction only after an object is initialized.
(I realize that I could condition on an attribute set during initialization,
but I am asking for the most elegant way to achieve this.)

Alan
You specifically excluded slots in your spec, but why? It's as close
to a working, elegant solution you will find. Nothing else will work,
and will be horribly inelegant.

You can still do it the above way, with nasty hacking (but not really
any nastier than what you're doing) by pushing the __setattr__ hook on
as the last part of your __init__. Of course, if they subclass and
don't call your base class __init__, then they can get by you. So
maybe you should inspect the call stack in your __setattr__ and only
allow instance setting if you're within your "approved" __init__
method. Even then, you can't stop them from grabbing your class,
removing or overwriting the hook, and creating classes on the fly.

I think at this point (actually quite a while before this point)
you're getting into silly amounts of thinking about it. Python for
consenting adults and all that.
Feb 27 '07 #10
OK, let me approach this from a new direction.
Suppose I define a class that behaves I think
as I stipulated::

class NothingNew:
a = 0
def __init__(self):
self.b = 1
self.initialized = True
def __setattr__(self, attr, val):
if not hasattr(self,'initialized') or hasattr(self, attr):
self.__dict__[attr] = val
else:
raise ValueError

After adding some documentation,
what are the downsides? (Assume a small
project where everyone knows this has been
done to the NothingNew class.) Anyone who wants the
behavior can keep it. Anyone who wants to add
stuff to an object just has to delete its
"initialized" attribute. Anyone who want other
behavior defined in this or a derived class can
just override __init__. Does this address most
of the (oddly passionate) objections?

Thanks,
Alan Isaac
Feb 28 '07 #11
"Alan Isaac" <ai****@american.eduwrites:
OK, let me approach this from a new direction. Suppose I define a
class that behaves I think as I stipulated::

class NothingNew:
a = 0
def __init__(self):
self.b = 1
self.initialized = True
def __setattr__(self, attr, val):
if not hasattr(self,'initialized') or hasattr(self, attr):
self.__dict__[attr] = val
else:
raise ValueError

After adding some documentation, what are the downsides?
The biggest downside I see is that you're going through contortions to
solve a problem you haven't demonstrated actually exists.

A smaller problem is that the ValueError should describe what's going wrong:

raise ValueError("The author of this class thinks you're up to no good")

Really, though, adding attributes to an instance is a normal thing to
do in Python, and you've not yet shown why you want that normal
functionality to be special-cased here.

--
\ "Experience is that marvelous thing that enables you to |
`\ recognize a mistake when you make it again." -- Franklin P. |
_o__) Jones |
Ben Finney

Feb 28 '07 #12
On Tue, 27 Feb 2007 20:59:03 +0000, Alan Isaac wrote:
"Steven D'Aprano" <st***@REMOVEME.cybersource.com.auwrote in message
news:pa****************************@REMOVEME.cyber source.com.au...
class Difficult(object):
def __setattr__(self, name, value):
if self.__dict__.has_key(name):
print "'%s' exists as an instance attribute" % name
self.__dict__[name] = value
elif self.__class__.__dict__.has_key(name):
print "'%s' exists as a class attribute" % name
self.__class__.__dict__[name] = value
else:
print "Can't create new attributes, 'cos I said so!"

But this prevents setting attributes during initialization,
so it does not meet the spec.
What, you expect us to do everything for you? *wink*

If you want the class to change behaviour after initialisation, you have
to code it to do so. The easy, but inelegant, way is to set a flag.

Finding an elegant way to do so is a little like asking for an elegant way
to scrub a septic tank clean. But one way might be to change the class
after initialisation:

class Parrot(object):
def __init__(self, data):
self.data = data
self.__class__ = Annoying

class Annoying(Parrot):
def __setattr__(self, name, value):
print "Annoy the user."
>>x = Parrot(5)
x.data
5
>>x.data = 7
Annoy the user.
>>x.data
5
--
Steven D'Aprano

Feb 28 '07 #13
Ben Finney writes:
Really, though, adding attributes to an instance is a normal thing to
do in Python, and you've not yet shown why you want that normal
functionality to be special-cased here.

I accept your earlier point that if an interface changes
one can just trap use of the old interface. But I remain
interested in preventing dynamic attribute creation in
particular cases.

I have not tried to argue that dynamic attribute creation
can be dangerous for a couple reasons.
- There is no reason to expect this list to be receptive.
- I do not have the experience to make a general argument.
- I do not always find it a problem, but only in certain situations.

However I will observe that
- entire languages are structured on the premise that dynamic
attribute creation can be hazardous
- debuggers watch out for dynamic attribute creation, which
tells us it is a common source of bugs
- I sincerely doubt that anyone who has written more than
a couple scripts in Python has never accidentally created an
attribute dynamically while intending to assign to an existing
attribute.

I know the response: write good unit tests. OK, but right now
I am writing code where this restriction will serve as a reasonable
error check, and the design I offered allows very easy removal
of the restriction in the future. Say, once I have written adequate
unit tests.

Alan
Feb 28 '07 #14

"Steven D'Aprano" <st***@REMOVEME.cybersource.com.auwrote in message
news:pa****************************@REMOVEME.cyber source.com.au...
The easy, but inelegant, way is to set a flag.
That is the approach I explored here:
http://mail.python.org/pipermail/pyt...ry/428562.html
Btw, I add some background comments about "why" here:
http://mail.python.org/pipermail/pyt...ry/428651.html

fwiw,
Alan Isaac
Feb 28 '07 #15
However I will observe that
- entire languages are structured on the premise that dynamic
attribute creation can be hazardous
Yup, and you are free to use one of them. And as an additional benefit, they
will be more performant because you then can optimize the code further.

But they certainly do need more code to accomplish the same things easily
done in python (or other dynamic languages for that matter)
- debuggers watch out for dynamic attribute creation, which
tells us it is a common source of bugs
- I sincerely doubt that anyone who has written more than
a couple scripts in Python has never accidentally created an
attribute dynamically while intending to assign to an existing
attribute.
Certainly that happened, but not in a number of occasions that its impact on
every day programming isn't dwarfed by the much more prevalent gain in
productivity using python.

You strive for the combination of disadvantages of statically typed
languages with the disadvantages of a dynamically typed one.

Not too much of an convincing goal IMHO.

Diez
Feb 28 '07 #16
Alan Isaac a écrit :
Ben Finney writes:
>>Really, though, adding attributes to an instance is a normal thing to
do in Python, and you've not yet shown why you want that normal
functionality to be special-cased here.

I accept your earlier point that if an interface changes
one can just trap use of the old interface. But I remain
interested in preventing dynamic attribute creation in
particular cases.

I have not tried to argue that dynamic attribute creation
can be dangerous for a couple reasons.
- There is no reason to expect this list to be receptive.
Right. Don't you wonder why ?
- I do not have the experience to make a general argument.
- I do not always find it a problem, but only in certain situations.
Which ones ?
However I will observe that
- entire languages are structured on the premise that dynamic
attribute creation can be hazardous
Entire languages are structured on the premise that developers are dumb
and that anything dynamic can be hazardous.

Seriously, if you fear dynamic attribute creation, you'd better choose
another language. What about dynamically changing the class of an
object? Now ain't *that* hazardous ?-)
- debuggers watch out for dynamic attribute creation,
Which ones ?
which
tells us it is a common source of bugs
s/common/potential/
- I sincerely doubt that anyone who has written more than
a couple scripts in Python has never accidentally created an
attribute dynamically while intending to assign to an existing
attribute.
Of course. But MHE (7+ years of Python programming - more than a couple
scripts...) is that this has not proven so far to be a common and
annoying enough problem to justify fighting against the language.
I know the response: write good unit tests. OK, but right now
I am writing code where this restriction will serve as a reasonable
error check,
I don't share your definition of "reasonable". But you should have
guessed by now !-)
Feb 28 '07 #17
En Wed, 28 Feb 2007 09:41:47 -0300, Alan Isaac <ai****@american.edu>
escribió:
However I will observe that
- entire languages are structured on the premise that dynamic
attribute creation can be hazardous
That's why we have so many languages to choose from. What is highly
important for someone is irrelevant for others. [PL/1 was destined to be
The Programming Language...]
If you actually dislike the concept of dynamic attribute creation, you're
using the wrong language.
- debuggers watch out for dynamic attribute creation, which
tells us it is a common source of bugs
Which debugger, please? Might be interesting. (As a debugging tool, not as
a design technique).
- I sincerely doubt that anyone who has written more than
a couple scripts in Python has never accidentally created an
attribute dynamically while intending to assign to an existing
attribute.
That *might* happen, but not so frequently as to fight against the dynamic
nature of Python.
I know the response: write good unit tests. OK, but right now
I am writing code where this restriction will serve as a reasonable
error check, and the design I offered allows very easy removal
of the restriction in the future. Say, once I have written adequate
unit tests.
If it suits your needs, fine! But you'll find that most Python programmers
won't agree with your approach.

--
Gabriel Genellina

Mar 1 '07 #18
Alan Isaac wrote:
class NothingNew:
a = 0
def __init__(self):
self.b = 1
self.initialized = True
There's a problem with that when you want to subclass:

class NothingElseNew(NothingNew):

def __init__(self):
NothingNew.__init__(self)
self.c = 42 # <--- Not allowed!

You could get around this by temporarily de-initializing
it, i.e.

def __init__(self):
NothingNew.__init__(self)
del self.__dict__['initialized']
self.c = 42
self.initialized = True

but that's not very elegant.

--
Greg
Mar 1 '07 #19
On Feb 26, 10:48 pm, "Alan Isaac" <ais...@american.eduwrote:
I have a class whose instances should only receive attribute
assignments for attributes that were created at inititialization.
If slots are not appropriate, what is the Pythonic design for this?

Thanks,
Alan Isaac
There is a Cookbook recipe for that:
http://aspn.activestate.com/ASPN/Coo.../Recipe/252158

Michele Simionato

Mar 1 '07 #20

"Bruno Desthuilliers" <bd*****************@free.quelquepart.frwrote in
message news:45***********************@news.free.fr...
I don't share your definition of "reasonable". But you should have
guessed by now
My view may be shaped by a different experience.
I have found dynamic attribute creation convenient
when doing something "quick and dirty", but I have
never wanted it as a design feature in a more serious
project.

Can you give me an example where design considerations
make dynamic attribute creation particularly desirable?

Thank you,
Alan Isaac
Mar 1 '07 #21

"greg" <gr**@cosc.canterbury.ac.nzwrote in message
news:54*************@mid.individual.net...
There's a problem with that when you want to subclass:
Agreed. The following addresses that and, I think, some
of the other objections that have been raised.
Alan

class Lockable:
a = 0
def __init__(self, lock=False):
self.b = 1
self.lock = lock
def __setattr__(self, attr, val):
if not hasattr(self,attr) and hasattr(self,'lock') and self.lock:
raise ValueError("This object accepts no new attributes.")
self.__dict__[attr] = val
Mar 1 '07 #22
On 28 Feb, 15:39, "Diez B. Roggisch" <d...@nospam.web.dewrote:
However I will observe that
- entire languages are structured on the premise that dynamic
attribute creation can be hazardous

Yup, and you are free to use one of them. And as an additional benefit, they
will be more performant because you then can optimize the code further.
I think that's something many people miss: it can be desirable to
declare the range of attributes on instances "up front" for
optimisation purposes (in various languages other than Python), but
the advantages of not risking the occasional AttributeError shouldn't
be too readily discarded either. I'm not sure where the different
tools stand on this front, but second-guessing invalid accesses to
attributes is a completely different level to just identifying dodgy
accesses to globals or locals, along with dubious imports and "bad
practice" programming styles consisting of one or more of those more
detectable things.

Paul

Mar 1 '07 #23
Alan Isaac a écrit :
"Bruno Desthuilliers" <bd*****************@free.quelquepart.frwrote in
message news:45***********************@news.free.fr...
>>I don't share your definition of "reasonable". But you should have
guessed by now


My view may be shaped by a different experience.
I have found dynamic attribute creation convenient
when doing something "quick and dirty", but I have
never wanted it as a design feature in a more serious
project.

Can you give me an example where design considerations
make dynamic attribute creation particularly desirable?
Let's consider an imaginary ORM (or it could be an "object-ldap mapper",
or just any other stuff where it may be useful to have 'smart
attributes', like Plone's Archetype or FormEncode or...):

class Person(Schema):
firstname = TextField(empty=False, format=str.capitalize)
lastname = TextField(empty=False, format=str.upper)
nickname = TextField(empty=False, format=str.lower, dbfield="login")

p = Person(firstname="joe", lastname="Hacker")

p.firstname
="Joe"
p.nickname
="joe"

p.fields['firstname'].dbfield
='firstname'

I let you try to write the SchemaType metaclass without dynamically
adding attributes to either the Person class (remember that classes are
objects too) and/or the TextField instances...
Let's consider the controller part of a web MVC application. You want to
manage security in a declarative way.

class PageController(Controller):
@requires_perm('ViewContent')
def view(self):
# code here

@requires_perm('EditContent'):
def edit(self):
# code here

etc...

What do you think this decorator will do ? yes, that's right: annotate
the action methods with needed perms, so the relevant part of the
framework can take appropriate action...

As a side note : in Python, *all* attributes are "dynamically created".
Class attributes are dynamically added to the class object by it's
metaclass, instance attributes are dynamically added to the instance,
usually - but not necessarily - by the __init__ method. Now the __init__
method is nothing more than a wrapper around the __init__ function,
which itself takes the (bare) instance as argument. Writing methods
within the class statement body makes things clearer, but is by no mean
mandatory:

class Foo(object):
pass

def init_foo(foo, name):
foo.name = name

Foo.__init__ = init_foo
Like a lot of other tools (HOFs, metaclasses, descriptors, generators
etc), dynamic attribute creation looks mostly like a useless gadget when
you never used it before - because it's not part of your "mind map", you
don't naturally use it in your design.

My 2 cents
Mar 1 '07 #24
The security application seems to call for roles.
I'll have to think about the schema example.
But in any case, my question was poorly stated.
I started out wanting to trap was the dynamic
addition of attributes to class instances after
initialization. While you responded to my later question
as asked, the response does not I think take this context
into account.

"Bruno Desthuilliers" <bd*****************@free.quelquepart.frwrote in
message news:45***********************@news.free.fr...
As a side note : in Python, *all* attributes are "dynamically created".
... Writing methods
within the class statement body makes things clearer, but is by no mean
mandatory:
Point taken. And I understand it means that my talk of
"after initialization" above contains some ambiguity.

One last point. While I remain interested in examples of how
"late" addition of attributes to class instances is useful,
I must note that everyone who responded agreed that it
has been a source of bugs. This seems to argue against a
general ban on "locking" objects in some way, in some
circumstances.

Thanks!
Alan
Mar 1 '07 #25
Alan Isaac a écrit :
The security application seems to call for roles.
Considering that a role is a set of permissions in a context. The
application doesn't want to say "you need this role", but "you need to
have a role that has this permission" (nb : this is based on Zope's
security model).
I'll have to think about the schema example.
But in any case, my question was poorly stated.
I started out wanting to trap was the dynamic
addition of attributes to class instances after
initialization.
From withing methods of the class, or from the outside world ? In the
first case, pylint is what you're looking for.
While you responded to my later question
as asked, the response does not I think take this context
into account.
Of course - this was not what I was answering to !-)

Now, in the above context, I still don't think your solution is the best
option.
>
>>As a side note : in Python, *all* attributes are "dynamically created".
... Writing methods
within the class statement body makes things clearer, but is by no mean
mandatory:


Point taken. And I understand it means that my talk of
"after initialization" above contains some ambiguity.
Indeed !-)
One last point. While I remain interested in examples of how
"late" addition of attributes to class instances is useful,
Then read the sources of packages / frameworks like SQLAlchemy,
SQLObject, FormEncode, CherryPy, TurboGears, Django, etc - and probably
a lot of other.

Heck, FWIW, modules are objects too - instances of class 'module', and
all the top-level names of a module are de facto examples of "late
addition of attributes to a class instance" !-)
I must note that everyone who responded agreed that it
has been a source of bugs.
Yes, of course - typos happens. But I've seen much more bugs introduced
by all the boilerplate and complexities needed to work around arbitrary
restrictions imposed by some well-known mainstream languages.

My experience with Python is that typos, type errors and all this class
of programmer error are usually *very* quickly noticed (ie : within
minutes).
This seems to argue against a
general ban on "locking" objects in some way, in some
circumstances.
I'd rather go for better (well... pylint is already a pretty nice tool)
code checkers.

My 2 cents...
Mar 1 '07 #26
>
One last point. While I remain interested in examples of how
"late" addition of attributes to class instances is useful,
I must note that everyone who responded agreed that it
has been a source of bugs. This seems to argue against a
general ban on "locking" objects in some way, in some
circumstances.

I've got some examples. I quite often used dynamic attributes in
situations where I wasn't the creator of certain objects, but had to use
them and wanted to link them with my own code.

For example, in various GUI-environments, you've got some sort of tree
view, usually containing item-objects that form the tree.

Now in other languages like C++ or Java, I have to subclass those items
(if possible at all) if I want to associate my own data (for example the
"real" object behind such an item) with them. Or I need to have an
awkward global mapping between tree-items and my data.

But in python, I can just easily add a new .data_object property, and in
whatever event-handler spits a e.g. selected item at me, I'm easily
accessing the data behind it.

Other examples are processing XML-trees that also have associated data
and the like.

Diez
Mar 1 '07 #27
On Feb 26, 9:48 pm, "Alan Isaac" <ais...@american.eduwrote:
I have a class whose instances should only receive attribute
assignments for attributes that were created at inititialization.
If slots are not appropriate, what is the Pythonic design for this?
Hi !

Even though a lot of people have argued against such a thing, I have
been thinking about this last night and I have the following hack.
Classes descending from SuspiciousObject below won't allow new
attributes to be added to their instances apart from within trusted
methods (i.e. methods decorated with @trustedmethod)

Note: this is probably not of great interest but I've decided to share
it since I did this as a result of reading this thread :)

class LockableDict(dict):
"A dict where addition of new keys can be prevented by setting the
locked attribute"
__slots__ = ('locked',)
def __init__(self, locked=False):
self.locked = locked
def force_setitem(self, key, value):
super(LockableDict, self).__setitem__(key, value)
def __setitem__(self, key, value):
if self.has_key(key) or not self.locked:
self.force_setitem(key, value)
else:
raise KeyError, key

def trustedmethod(f):
def pf(self, *args, **kwargs):
was_locked = self.__dict__.locked
self.__dict__.locked = False
try:
return f(self, *args, **kwargs)
finally:
self.__dict__.locked = was_locked
return pf

class SuspiciousObject(object):
def __new__(cls, *args, **kwargs):
self = object.__new__(cls)
super(SuspiciousObject, self).__setattr__('__dict__',
LockableDict(locked=True))
return self
def __setattr__(self, attr, value):
try:
self.__dict__[attr] = value
except KeyError:
raise AttributeError, "'%s' object has no attribute '%s" %
(type(self).__name__, attr)

# Instances of SuspiciousObject refuse anyone the right to create a
new attribute apart from methods marked with the decorator
@trustedmethod.
# Example:

class Foo(SuspiciousObject):
@trustedmethod
def __init__(self):
self.bar = 2
self.baz = 'Hello'
def foobar(self, v):
self.fubar = v
@trustedmethod
def force_setattr(self, attr, val=None):
setattr(self, attr, val)

This would give something like:
>>foo=Foo()
foo.bar
2
>>foo.baz
'Hello'
>>foo.baz="Bye" # works as foo.baz exists
foo.baz
'Bye'
>>foo.fubar=4 # won't be trusted
....
AttributeError: 'Foo' object has no attribute 'fubar
>>setattr(foo, 'fubar', 4) # won't work either
....
AttributeError: 'Foo' object has no attribute 'fubar
>>foo.__dict__['fubar']=4 # Neither will this
....
KeyError: 'fubar'
>>foo.foobar(4) # Neither will this as foo.foobar is not a trusted method
....
AttributeError: 'Foo' object has no attribute 'fubar
>>foo.force_setattr('fubar', 4) # this will work as force_setattr is trusted
foo.fubar
4
>># etc...
By creating a custom metaclass for SuspiciousObject, it would be easy
to make SuspiciousObjects trust their own methods by default instead
of having to declare which method are trusted.

--
Arnaud

Mar 4 '07 #28
Hi Alan,
One last point. While I remain interested in examples of how
"late" addition ofattributesto class instances is useful,
I must note that everyone who responded agreed that it
has been a source of bugs. This seems to argue against a
general ban on "locking" objects in some way, in some
circumstances.
If you want to restrict "late" addition of attributes, no-one will
prevent you to do so.
Arnaud has already given you an example implementation. Here is mine:
>>class Foo(object) :
....
.... _locked = False
....
.... def __setattr__(self,attr,var) :
.... if self._locked and not attr in dir(self):
.... raise RuntimeError
.... else :
.... object.__setattr__(self,attr,var)
....
.... def lock(self) :
.... self._locked = True
....
>>foo = Foo()
foo.bar = 'allowed'
foo.lock()
foo.spam = 'fails'
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in __setattr__
NotImplementedError
>>>
foo.bar = 'still works'

See how it works? The lock method *dynamically* adds the attribute
foo._locked *after* initialization to the instance. Before the call
of foo.lock() foo._locked is a class attribute. Now you might argue
that one should better set foo._locked = False in the __init__ method
rather than as a class attribute. Something like:

class Foo(object) :
def __init__(self) :
self._locked = False

But no! The initialization would trigger
Foo.__setattr__(foo,'_locked',False)
which naturally runs into an attribute error since __setattr__ looks
up this
attribute. So this very same implementation is one of the pro examples
you
asked for :-)
cheers,

- harold -

Mar 15 '07 #29

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
by: Danimal | last post by:
I have been using PHP for a long time... since it was called PHP/FI. I have a programming design question: Let say I have this class: class attrib { var $lenght; var $type; ... }
2
by: Anders | last post by:
We need to create a lookup structure that can contain a flexible amount of attributes. We have discussed different design solutions for lookup-functionality, and come up with this suggestion: ...
6
by: Tim Mavers | last post by:
I have a series of database objects that represent things such as people, accounts, etc. I have a set of options (boolean) that I need to add to these objects. Normally I would just create a bit...
9
by: sk | last post by:
I have an applicaton in which I collect data for different parameters for a set of devices. The data are entered into a single table, each set of name, value pairs time-stamped and associated with...
17
by: tshad | last post by:
Many (if not most) have said that code-behind is best if working in teams - which does seem logical. How do you deal with the flow of the work? I have someone who is good at designing, but...
3
by: Allerdyce.John | last post by:
I have a design type of quesiton. What is the advantages of using accessor (a getter/setter method) instead of making the attribute 'public'? If a class has public accessor (a getter/setter...
11
by: manstey | last post by:
Hi, I am having trouble designing my classes. I have two classes. The first one wraps around an old-style class called oref Class CacheClass(object): def __init__(self, obj):
9
by: mliptak | last post by:
Hi, I'm implementing a class which will serve as a callback. It will have multiple virtual operations, which will be called upon certain events. The user of the class can inherit from this class...
12
by: nyathancha | last post by:
Hi, I have a question regarding best practices in database design. In a relational database, is it wise/necessary to sometimes create tables that are not related to other tables through a...
0
by: MeoLessi9 | last post by:
I have VirtualBox installed on Windows 11 and now I would like to install Kali on a virtual machine. However, on the official website, I see two options: "Installer images" and "Virtual machines"....
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
by: Aftab Ahmad | last post by:
Hello Experts! I have written a code in MS Access for a cmd called "WhatsApp Message" to open WhatsApp using that very code but the problem is that it gives a popup message everytime I clicked on...
0
by: Aftab Ahmad | last post by:
So, I have written a code for a cmd called "Send WhatsApp Message" to open and send WhatsApp messaage. The code is given below. Dim IE As Object Set IE =...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: marcoviolo | last post by:
Dear all, I would like to implement on my worksheet an vlookup dynamic , that consider a change of pivot excel via win32com, from an external excel (without open it) and save the new file into a...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.