473,399 Members | 3,038 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,399 software developers and data experts.

Dynamic use of property() fails

Hi,

This is my first attempt at new classes and dynamic python, so I am
probably doing something very stupid... After reading the how-to for
descriptors at http://users.rcn.com/python/download/Descriptor.htm I
decided I would make an object that returns attributes on read, but on
setting calls an arbitrary function.

My code looks like:
class ActiveDAO(object):
def __init__(self):
self.__values__ = {}
def add_field(self, name, value, on_change):
self.__values__[name] = value
def get(self): return self.__values__[name]
def set(self, new_value): self.__values__[name] =
on_change(new_value)
def delete(self): raise AttributeError
self.__dict__[name] = property(get, set, delete)

However, when I try to use this (in a test) with code like:
dao = ActiveDAO()
dao.add_field("name", "value", lambda _: None)
assertEqual(dao.name, "value")

I get a failure because lookup of the attribute is returning
"<property object at 0x6b8910>".

That is quite reasonable, but I was under the expression that some
magic was supposed to happen, as described in the document referenced
above!

Please can someone explain why there is no magic? :o(

Thanks,
Andrew

PS I am using Python 2.5 on Linux
Jun 27 '08 #1
12 1680
En Mon, 14 Apr 2008 22:43:39 -0300, andrew cooke <an****@acooke.org>
escribió:
This is my first attempt at new classes and dynamic python, so I am
probably doing something very stupid... After reading the how-to for
descriptors at http://users.rcn.com/python/download/Descriptor.htm I
decided I would make an object that returns attributes on read, but on
setting calls an arbitrary function.

My code looks like:
class ActiveDAO(object):
def __init__(self):
self.__values__ = {}
def add_field(self, name, value, on_change):
self.__values__[name] = value
def get(self): return self.__values__[name]
def set(self, new_value): self.__values__[name] =
on_change(new_value)
def delete(self): raise AttributeError
self.__dict__[name] = property(get, set, delete)

However, when I try to use this (in a test) with code like:
dao = ActiveDAO()
dao.add_field("name", "value", lambda _: None)
assertEqual(dao.name, "value")

I get a failure because lookup of the attribute is returning
"<property object at 0x6b8910>".

That is quite reasonable, but I was under the expression that some
magic was supposed to happen, as described in the document referenced
above!
The "magic" happens when the descriptor is found in the *class*, not in
the instance. I think it's detailed in Hettinger's document.
Do you actually want "per-instance" defined properties?

__special__ names are reserved for Python internal usage; don't use them.
Implementation-only attributes ("private" ones) are spelled with a single
underscore.
--
Gabriel Genellina

Jun 27 '08 #2
[Gabriel:]
The "magic" happens when the descriptor is found in the *class*, not in
the instance. I think it's detailed in Hettinger's document.
Do you actually want "per-instance" defined properties?
ah! ok. yes, you're right: i want instance values but class
properties, so i'll rethink things. thanks very much!
__special__ names are reserved for Python internal usage; don't use them.
Implementation-only attributes ("private" ones) are spelled with a single
underscore.
ah, yes, sorry about that.

thanks again for the quick reply,
andrew
Jun 27 '08 #3
"Gabriel Genellina" <ga*******@yahoo.com.arwrites:
The "magic" happens when the descriptor is found in the *class*, not
in the instance. I think it's detailed in Hettinger's document.
The document is wrong here:

Alternatively, it is more common for a descriptor to be invoked
automatically upon attribute access. For example, obj.d looks up d
in the dictionary of obj. If d defines the method __get__, then
d.__get__(obj) is invoked according to the precedence rules listed
below.

This sounds plausible and might have led Andrew to believe that his
code should work. It should instead say "in the dictionary of obj's
type" or something to that effect. I asked Raymond about it some time
ago, and he agreed that it's an error, but he apparently didn't get
around to fixing it.

The actual code examples in the document are, of course, correct.
Jun 27 '08 #4
andrew cooke <an****@acooke.orgwrites:
This is my first attempt at new classes and dynamic python, so I am
probably doing something very stupid... After reading the how-to for
descriptors at http://users.rcn.com/python/download/Descriptor.htm I
decided I would make an object that returns attributes on read, but on
setting calls an arbitrary function.

My code looks like:
class ActiveDAO(object):
def __init__(self):
self.__values__ = {}
def add_field(self, name, value, on_change):
self.__values__[name] = value
def get(self): return self.__values__[name]
def set(self, new_value): self.__values__[name] =
on_change(new_value)
def delete(self): raise AttributeError
self.__dict__[name] = property(get, set, delete)
As others explained, descriptors are called for descriptors found in
class attributes, not in ones in instance attributes. Calling them
for the latter would be dangerous because it might accidentally invoke
magic whenever you store the "wrong" kind of object in an instance
attribute. Also, in many cases (slots), instance property access is
*implemented* using class property descriptors, so calling descriptors
on objects retrieved from the instance would mean that the descriptor
would have to be invoked twice.

However, if you know what you're doing, you can simply customize your
class's __getattribute__ to do what *you* want for your objects. For
example:

def __getattribute__(self, name):
dict = object.__getattribute__(self, '__dict__') # self.__dict__ would infloop
if name in dict:
o = dict[name]
# call the descriptor even if found in an object in __dict__
if hasattr(o, '__get__'):
return o.__get__(self, type(self))
return o
return object.__getattribute__(self, name)

With that addition:
>>dao = ActiveDAO()
dao.add_field('name', 'value', lambda _: None)
dao.name
'value'
>>dao.__dict__['name']
<property object at 0xb7d53284>
Jun 27 '08 #5
andrew cooke a écrit :
Hi,

This is my first attempt at new classes and dynamic python, so I am
probably doing something very stupid... After reading the how-to for
descriptors at http://users.rcn.com/python/download/Descriptor.htm I
decided I would make an object that returns attributes on read, but on
setting calls an arbitrary function.

My code looks like:
class ActiveDAO(object):
def __init__(self):
self.__values__ = {}
__names__ are reserved for the Python implementation itself. Use _names
for 'protected' attributes.
def add_field(self, name, value, on_change):
self.__values__[name] = value
def get(self): return self.__values__[name]
def set(self, new_value): self.__values__[name] =
on_change(new_value)
def delete(self): raise AttributeError
self.__dict__[name] = property(get, set, delete)

However, when I try to use this (in a test) with code like:
dao = ActiveDAO()
dao.add_field("name", "value", lambda _: None)
assertEqual(dao.name, "value")

I get a failure because lookup of the attribute is returning
"<property object at 0x6b8910>".

That is quite reasonable, but I was under the expression that some
magic was supposed to happen, as described in the document referenced
above!

Please can someone explain why there is no magic? :o(
Others already answered this.

The canonical solution is to use a custom descriptor instead of a property:
class Field(object):
def __init__(self, name, onchange):
self.name = name
self.onchange = onchange

def __get__(self, instance, cls):
if instance is None:
# called on the class
return self
# called on instance
return instance._values[self.name]

def __set__(self, instance, value):
instance._values[name] = self.onchange(value)

class ActiveDAO(object):
def __init__(self):
self._values = []
class Person(ActiveDAO):
name = Field('firstname', lambda v: v.strip().capitalize())
age = Field('age', lambda v : int(v))

Now you may want to search here or in the cookbook to learn how to:
- dynamically create new classes
- avoid having to repeat the name of the field (usually done using a
metaclass and a two-stages initialisation of Field objects)
HTH
Jun 27 '08 #6
Hrvoje Niksic a écrit :
(snip)
As others explained, descriptors are called for descriptors found in
class attributes, not in ones in instance attributes.
(snip)
However, if you know what you're doing, you can simply customize your
class's __getattribute__ to do what *you* want for your objects.
<op>
But bear in mind that, beside possible unwanted side-effectn, you'll get
a non-negligible performance hit - __getattribute__ being, as the name
implies, invoked on each and every attribute lookup.

FWIW, I tried this approach once, and quickly came back to a simpler
solution.

</op>
(snip code)
Jun 27 '08 #7
On Apr 15, 4:06 am, Bruno Desthuilliers <bruno.
42.desthuilli...@websiteburo.invalidwrote:
The canonical solution is to use a custom descriptor instead of a property:

class Field(object):
def __init__(self, name, onchange):
self.name = name
self.onchange = onchange

def __get__(self, instance, cls):
if instance is None:
# called on the class
return self
# called on instance
return instance._values[self.name]

def __set__(self, instance, value):
instance._values[name] = self.onchange(value)

class ActiveDAO(object):
def __init__(self):
self._values = []

class Person(ActiveDAO):
name = Field('firstname', lambda v: v.strip().capitalize())
age = Field('age', lambda v : int(v))
i tried code very similar after reading the first replies and found
that it did not work as expected on setting. for example, in

person = Person()
person.age = 27

"age" is set in the instance's dictionary (as 27; the descriptor is
not called), which then shadows the definition of age in the class
dictionary.

my understanding was that the descriptor is only called in the class
context, so would be called if, say, a subclass tried to redefine
age. but maybe i am still confused.

i am about to go to sleep. i guess i will try your code exactly
tomorrow, but it looks very close to mine which showed this problem.
are you sure your solution works?

thanks,
andrew
Jun 27 '08 #8

ignore that - i was mistaken (my test was too complex).

the problem seems to be that the attribute is deleted even though
__delete__ is defined.

i'll look at it tomorrow.

thanks again,
andrew

On Apr 15, 4:50 am, andrew cooke <and...@acooke.orgwrote:
i tried code very similar after reading the first replies and found
that it did not work as expected on setting. for example, in

person = Person()
person.age = 27

"age" is set in the instance's dictionary (as 27; the descriptor is
not called), which then shadows the definition of age in the class
dictionary.

my understanding was that the descriptor is only called in the class
context, so would be called if, say, a subclass tried to redefine
age. but maybe i am still confused.

i am about to go to sleep. i guess i will try your code exactly
tomorrow, but it looks very close to mine which showed this problem.
are you sure your solution works?

thanks,
andrew
Jun 27 '08 #9
OK, fixed my bug - it does work. Now sleep... Thanks again, Andrew
Jun 27 '08 #10
andrew cooke a écrit :
On Apr 15, 4:06 am, Bruno Desthuilliers <bruno.
42.desthuilli...@websiteburo.invalidwrote:
>The canonical solution is to use a custom descriptor instead of a property:
(snip code)
i tried code very similar after reading the first replies and found
that it did not work as expected on setting. for example, in

person = Person()
person.age = 27

"age" is set in the instance's dictionary (as 27; the descriptor is
not called), which then shadows the definition of age in the class
dictionary.
Are you sure your Person class is a new-style one (inheriting, directly
or not, from object) ? The descriptor protocol doesn't work properly on
old-style classes, with *exactly* the symptom you describe here (setter
is not invoked, property get shadowed by an instance attribute)
my understanding was that the descriptor is only called in the class
context,
The descriptor protocol is only invoked on class attributes.
so would be called if, say, a subclass tried to redefine
age. but maybe i am still confused.
Possibly.
i am about to go to sleep. i guess i will try your code exactly
tomorrow, but it looks very close to mine which showed this problem.
are you sure your solution works?
Yes - minus a couple (unrelated) typos ('name' instead of 'self.name' in
Field.__set__, and 'self._values = []' instead of 'self._values = {}' in
ActiveDAO.__init__). Here's the corrected one:

class Field(object):
def __init__(self, name, onchange):
self.name = name
self.onchange = onchange

def __get__(self, instance, cls):
if instance is None:
# called on the class
return self
# called on instance
return instance._values[self.name]

def __set__(self, instance, value):
instance._values[self.name] = self.onchange(value)

class ActiveDAO(object):
def __init__(self):
self._values = {}

class Person(ActiveDAO):
name = Field('name', lambda v: v.strip().capitalize())
age = Field('age', lambda v : int(v))

Jun 27 '08 #11
Bruno Desthuilliers <br********************@websiteburo.invalid>
writes:
>However, if you know what you're doing, you can simply customize your
class's __getattribute__ to do what *you* want for your objects.

<op>
But bear in mind that, beside possible unwanted side-effectn, you'll
get a non-negligible performance hit - __getattribute__ being, as the
name implies, invoked on each and every attribute lookup.
That's unavoidable, though -- whatever you do to customize your class
in Python, the result will be slower than the C code built into
Python. Fortunately, not every object is performance-critical. In
this case, __getattribute__ buys you per-instance customization not
otherwise available. The code you posted is probably more efficient,
but at the cost of losing the ability to customize specific instances
of the class.
Jun 27 '08 #12
Hrvoje Niksic a écrit :
Bruno Desthuilliers <br********************@websiteburo.invalid>
writes:
>>However, if you know what you're doing, you can simply customize your
class's __getattribute__ to do what *you* want for your objects.
<op>
But bear in mind that, beside possible unwanted side-effectn, you'll
get a non-negligible performance hit - __getattribute__ being, as the
name implies, invoked on each and every attribute lookup.

That's unavoidable, though -- whatever you do to customize your class
in Python, the result will be slower than the C code built into
Python.
This one customization hook is probably the most sensible still.
Fortunately, not every object is performance-critical. In
this case, __getattribute__ buys you per-instance customization not
otherwise available. The code you posted is probably more efficient,
but at the cost of losing the ability to customize specific instances
of the class.
You could as well just customize __setattr__/__getattr__ (which is what
was done before new-style classes and descriptors). But my own
experience is that the very few times I thought I had a need for
per-instance descriptors, I found other solutions that were certainly
easier to grasp and maintain on the long term. Not that I would not be
able to deal with per-instance descriptors, but so far the cost
outweighted the benefits IMHO. Now YMMV of course !-)
Jun 27 '08 #13

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

Similar topics

3
by: JDPope | last post by:
I have a situation which I cannot get a good lead on how to resolve. One of the applications I support uses the Hibernate software to generate SQL. The app is JAVA with JDBC. In testing the users...
5
by: MGFoster | last post by:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 I've converted an ACC97 .mdb file to an ACC2K2 .adp. A report that worked in ACC97 doesn't work in ACC2K2. Report setup: ACC97 ...
1
by: Nathan Bloomfield | last post by:
Does anyone know if there is any documentation which relates to Access2k + ? or can anyone help adjust the code? I am having trouble converting the DAO references. TITLE :INF: How to...
3
by: CAD Fiend | last post by:
Hello, Well, after an initial review of my database by my client, they have completely changed their minds about how they want their form. As a result, I'm having to re-think the whole process....
3
by: MikeY | last post by:
Hi Everyone, I am working in C#, windows forms.My question is this. All my button dynamic controls properties are present and accounted for except for the"FlatStyle" properties. I can't seem to...
4
by: Mike | last post by:
I get this error when trying to use a paramter on a dynamic sql statement. What am I doing wrong. Exception Details: System.Data.OracleClient.OracleException: ORA-01036: illegal variable...
10
by: Zytan | last post by:
This article: http://www.c-sharpcorner.com/UploadFile/mahesh/WorkingWithArrays11142005060354AM/WorkingWithArrays.aspx claims that "In C#, arrays can be declared as fixed length or dynamic". I...
1
by: Peterwkc | last post by:
Hello all expert, i have two program which make me desperate bu after i have noticed the forum, my future is become brightness back. By the way, my problem is like this i the first program was...
9
by: Piotr K | last post by:
Ok, I tried simply everything that came to my mind and now I ran out of ideas, but to the point - take a look at the code below // GetStyle returns given style value (works fine)...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

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.