(newbie) Is there a way to prevent "name redundancy" in OOP ?

S

Stef Mientki

Not sure I wrote the subject line correct,
but the examples might explain if not clear


*** first attempt ***
class pin:
def __init__ (self):
self.Name = 'Unknown Pin'

aap = pin() # create an instance
aap.Name = 'aap' # set it's name
print aap.Name # print it's name
# but why should I set it's name ??
print 'aap' # I can just as well print a constant string !!
# (ok there will be an extra check)


*** second attempt ***
class pin2:
def __init__ (self, naam):
self.Name = naam

aap2 = pin2('aap2') # seems completely redundant to me.
print aap2.Name
print 'aap2'


Can this be achieved without redundancy ?

thanks,
Stef Mientki
 
L

Laszlo Nagy

Stef said:
Not sure I wrote the subject line correct,
but the examples might explain if not clear


*** first attempt ***
class pin:
def __init__ (self):
self.Name = 'Unknown Pin'

aap = pin() # create an instance
aap.Name = 'aap' # set it's name
print aap.Name # print it's name

# but why should I set it's name ??
print 'aap' # I can just as well print a constant string !!
# (ok there will be an extra check)
If you are trying to determine the name of your object, then you do not
know what 'name' and 'object' means in Python. In this case, I recommend
you this:

http://effbot.org/zone/python-objects.htm

In particular, pay attention to that objects can have zero or more names.

This is how you can create three unnamed objects:

L = [pin(),pin(),pin(),]

This is how you can bind three names to the same object:

p1 = p2 = p3 = pin()

Maybe I did not understand you question.

Laszlo
 
F

Fuzzyman

Stef said:
Not sure I wrote the subject line correct,
but the examples might explain if not clear


*** first attempt ***
class pin:
def __init__ (self):
self.Name = 'Unknown Pin'

aap = pin() # create an instance
aap.Name = 'aap' # set it's name
print aap.Name # print it's name
# but why should I set it's name ??
print 'aap' # I can just as well print a constant string !!
# (ok there will be an extra check)


*** second attempt ***
class pin2:
def __init__ (self, naam):
self.Name = naam

aap2 = pin2('aap2') # seems completely redundant to me.
print aap2.Name
print 'aap2'


Can this be achieved without redundancy ?

Uhm.. if your code can work with a constant then creating an object to
hold it as an attribute is redundant.

If on the other hand you want an object to hold several attributes, and
methods for working with them, and then want to pass these objects
around your code with a single reference - then maybe an object is the
best way.

If you don't need custom classes then don't use them...

Fuzzyman
http://www.voidspace.org.uk/python/articles.shtml
 
J

Jorge Vargas

if I undestand correctly this is what you want
.... def __init__(self,name):
.... self.Name = name
.... def __str__(self):
.... return self.Name
....Traceback (most recent call last):
aaa
 
C

Carl Banks

Stef said:
Not sure I wrote the subject line correct,
but the examples might explain if not clear


*** first attempt ***
class pin:
def __init__ (self):
self.Name = 'Unknown Pin'

aap = pin() # create an instance
aap.Name = 'aap' # set it's name
print aap.Name # print it's name
# but why should I set it's name ??
print 'aap' # I can just as well print a constant string !!
# (ok there will be an extra check)


*** second attempt ***
class pin2:
def __init__ (self, naam):
self.Name = naam

aap2 = pin2('aap2') # seems completely redundant to me.
print aap2.Name
print 'aap2'


Can this be achieved without redundancy ?

No. Simply not possible in Python.

You should consider whether there is another opportunity to eliminate
the redundancy. For instance, it looks like these pin definitions
might end up in a dictionary of some sort (I don't know your use case).
You could automatically generate the keys from the names like this:

_pindefs = (
pin("aap"),
pin("wap"),
pin("dcr"),
...
)
pins = {}
for pin in _pindefs:
pins[pin.Name] = pin

Then you access pins["aap"]. The reason I suspect the pins will end up
in a dictionary is that, in many real applications, the pin name would
often be specified in the input. (This is very often the case whenever
people want to know the symbol an object is bound to, for any
application.)


Carl Banks
 
M

Martin Miller

Stef said:
Not sure I wrote the subject line correct,
but the examples might explain if not clear


*** first attempt ***
class pin:
def __init__ (self):
self.Name = 'Unknown Pin'

aap = pin() # create an instance
aap.Name = 'aap' # set it's name
print aap.Name # print it's name
# but why should I set it's name ??
print 'aap' # I can just as well print a constant string !!
# (ok there will be an extra check)

While I agree that it's likely you're confusing Python objects and
names, Python *is* an interpreted language and therefore very flexible.
Here's a snippet showing one way to remove the 'redundancy'. (Be
forewarned that doing things like this is highly distasteful to some
people.)

### non-redundant example ###
import sys

class Pin:
def __init__(self, name, namespace=None):
self.name = name
if namespace == None:
# default to caller's globals
namespace = sys._getframe(1).f_globals
namespace[name] = self

Pin('aap') # create a Pin object named 'aap'
Pin('aap2') # create a Pin object named 'aap2'
print aap.name
print aap2.name

-Martin
 
C

Carl Banks

Martin said:
### non-redundant example ###
import sys

class Pin:
def __init__(self, name, namespace=None):
self.name = name
if namespace == None:
# default to caller's globals
namespace = sys._getframe(1).f_globals
namespace[name] = self

Pin('aap') # create a Pin object named 'aap'
Pin('aap2') # create a Pin object named 'aap2'
print aap.name
print aap2.name

The problem with this is that it only works for global namespaces,
while failing silently and subtly if used in a local namespace:

def fun():
Pin('aap')
aap1 = aap
fun2()
aap2 = aap
print aap1 is aap2

def fun2():
Pin('aap')

If it's your deliberate intention to do it with the global namespace,
you might as well just use globals() and do it explicitly, rather than
mucking around with Python frame internals. (And it doesn't make the
class unusable for more straightforward uses.)

for _name in ('aap','dcr'):
globals()[_name] = Pin(_name)
del _name


Carl Banks
 
S

Steven Bethard

Stef said:
Not sure I wrote the subject line correct,
but the examples might explain if not clear [snip]
class pin2:
def __init__ (self, naam):
self.Name = naam

aap2 = pin2('aap2') # seems completely redundant to me.
print aap2.Name

You can use class statements to create your instances with some
metaclass trickery. Unlike normal assignments, class statements get
access to the name being assigned. So you could write your class like::
... def __init__(self, name):
... self.name = name
... @classmethod
... def from_class_block(cls, name, bases, block_dict):
... return cls(name)
...

And then use class statements to create instances of the ``pin`` class::
... __metaclass__ = pin.from_class_block
... <class '__main__.pin'>

Of course, using class statements to create *instances* of a class is
confusing. And it's more verbose than simply repeating the name when you
call the constructor. But at least it avoids the redundancy. ;-)

STeVe
 
S

Stef Mientki

Steven said:
Stef said:
Not sure I wrote the subject line correct,
but the examples might explain if not clear [snip]
class pin2:
def __init__ (self, naam):
self.Name = naam

aap2 = pin2('aap2') # seems completely redundant to me.
print aap2.Name

You can use class statements to create your instances with some
metaclass trickery. Unlike normal assignments, class statements get
access to the name being assigned. So you could write your class like::

thank you all guys,
this gives me a lot to study,
for the moment I'm satisfied with the fact that it "is possible".

cheers,
Stef Mientki
 
M

Martin Miller

Carl said:
Martin said:
### non-redundant example ###
import sys

class Pin:
def __init__(self, name, namespace=None):
self.name = name
if namespace == None:
# default to caller's globals
namespace = sys._getframe(1).f_globals
namespace[name] = self

Pin('aap') # create a Pin object named 'aap'
Pin('aap2') # create a Pin object named 'aap2'
print aap.name
print aap2.name

The problem with this is that it only works for global namespaces,
while failing silently and subtly if used in a local namespace:

Oh, contrair. It would work fine with local namespaces simply by
overriding the default value of the optional 'namepace' parameter (see
below).
def fun():
Pin('aap')
aap1 = aap
fun2()
aap2 = aap
print aap1 is aap2

def fun2():
Pin('aap')

If it's your deliberate intention to do it with the global namespace,
you might as well just use globals() and do it explicitly, rather than
mucking around with Python frame internals. (And it doesn't make the
class unusable for more straightforward uses.)

You could be more explicit by just passing 'globals()' as a second
parameter to the __init__ constructor (which is unnecessary, since
that's effectively the default).

It's not clear to me how the example provided shows the technique
"failing silently and subtly if used in a local namespace" because what
happens is exactly what I would expect if the constructor is called
twice with the same string and defaulted namespace -- namely create
another object and make the existing name refer to it. If one didn't
want the call to Pin in fun2 to do this, just change fun2 to this:

def fun2():
Pin('aap', locals())

This way the "print aap1 is aap2" statement in fun() would output
"true" since the nested call to Pin would now (explicitly) cause the
name of the new object to be put into fun2's local namespace leaving
the global one created by the call in fun() alone.

Obviously, this was not an objection I anticipated. An important one I
would think is the fact that the current documentation says that
f_globals is a special *read-only* attribute of a frame object implying
that it shouldn't be changed (even though doing so 'works' as my
example illustrates).

I think other valid arguments against this practice might include
whether it's an example of good OOP, or a good programming practice at
all, since it involves possibly subtle side-effects, the use of global
variables, and/or is 'unpythonic'.

Certainly the approach is not without caveats. Despite them, I believe
it can be useful in some contexts, as long as what is going on is
clearly understood. The point of my reply to the OP was mainly just to
illustrate the power and flexibility of Python to the newbie. I guess
to that I should have also added that it gives you "enough rope to
shoot yourself" as Allen Holub said regarding C++.

Cheers,
-Martin
 
M

Martin Miller

Bruno said:
Martin Miller a écrit :
(snip)

I guess you mean "au contraire" ?-)

(snip)

FWIW "contrair" is how it's spelled in the Oxford English dictionary (I
actually did look it up before posting because it seemed like there
ought be an 'e' on the end). The dictionary also says it's chiefly
Scottish but the etymology indicates from the Old French "contraire".

Technically I suppose I should have written "Oh, *on* contrair" or
better yet just "On the contrary". ;-)

-Martin
 
B

Bruno Desthuilliers

Martin Miller a écrit :
FWIW "contrair" is how it's spelled in the Oxford English dictionary (I
actually did look it up before posting because it seemed like there
ought be an 'e' on the end). The dictionary also says it's chiefly
Scottish but the etymology indicates from the Old French "contraire".

Not that 'old' !-) It's still a common French word. "au contraire" is
French for "on the contrary", and "au" is prononced 'o' - hence my mistake.
 
C

Carl Banks

Martin said:
Carl said:
Martin said:
### non-redundant example ###
import sys

class Pin:
def __init__(self, name, namespace=None):
self.name = name
if namespace == None:
# default to caller's globals
namespace = sys._getframe(1).f_globals
namespace[name] = self

Pin('aap') # create a Pin object named 'aap'
Pin('aap2') # create a Pin object named 'aap2'
print aap.name
print aap2.name

The problem with this is that it only works for global namespaces,
while failing silently and subtly if used in a local namespace:

Oh, contrair. It would work fine with local namespaces simply by
overriding the default value of the optional 'namepace' parameter (see
below).

Did you try it?
You could be more explicit by just passing 'globals()' as a second
parameter to the __init__ constructor (which is unnecessary, since
that's effectively the default).

It's not clear to me how the example provided shows the technique
"failing silently and subtly if used in a local namespace" because what
happens is exactly what I would expect if the constructor is called
twice with the same string and defaulted namespace -- namely create
another object and make the existing name refer to it. If one didn't
want the call to Pin in fun2 to do this, just change fun2 to this:

Because the usage deceptively suggests that it defines a name in the
local namespace. Failing may be too strong a word, but I've come to
expect a consistent behavior w.r.t. namespaces, which this violates, so
I think it qualifies as a failure.
def fun2():
Pin('aap', locals())

Did you actually try this? Better yet, try this function. Make sure
aap isn't defined as a global:

def fun3():
Pin('aap',locals())
print aap

(I get NameError.)
This way the "print aap1 is aap2" statement in fun() would output
"true" since the nested call to Pin would now (explicitly) cause the
name of the new object to be put into fun2's local namespace leaving
the global one created by the call in fun() alone.

Did you try it?
Obviously, this was not an objection I anticipated. An important one I
would think is the fact that the current documentation says that
f_globals is a special *read-only* attribute of a frame object implying
that it shouldn't be changed (even though doing so 'works' as my
example illustrates).

That means it can't be rebound. It doesn't mean you can't modify the
object. Nevertheless, frame objects are an internal feature of Python,
not guaranteed to work for other incarnations of Python (it doesn't
even exist in IronPython, for example), and subject to change. And
what you want to do is easily done in a platform generic way without
using sys._getframe.
I think other valid arguments against this practice might include
whether it's an example of good OOP, or a good programming practice at
all, since it involves possibly subtle side-effects, the use of global
variables, and/or is 'unpythonic'.

I think programmatically creating variables is fine; I just recommend
you not use sys._getframe, nor the automagical namespace self-insertion
class, to do it.
Certainly the approach is not without caveats. Despite them, I believe
it can be useful in some contexts, as long as what is going on is
clearly understood. The point of my reply to the OP was mainly just to
illustrate the power and flexibility of Python to the newbie. I guess
to that I should have also added that it gives you "enough rope to
shoot yourself" as Allen Holub said regarding C++.
Cheers,
-Martin



Carl Banks
 
?

=?ISO-8859-1?Q?=22Martin_v=2E_L=F6wis=22?=

Stef said:
Can this be achieved without redundancy ?

You can use the registry design to use the object's name also to find
the object. In the most simple way, this is

registry = {}

class pin:
def __init__(self, name):
registry[name] = self
self.name = name

pin('aap')
print registry['aap']

Using computed attribute names, you can reduce typing on attribute
access:

class Registry: pass
registry = Registry()


class pin:
def __init__(self, name):
setattr(registry, name, self)
self.name = name

pin('aap')
print registry.aap

If you want to, you can combine this with the factory/singleton
patterns, to transparently create the objects on first access:

class Registry:
def __getitem__(self, name):
# only invoked when attribute is not set
r = pin(name)
setattr(self, name, r)
return r
registry = Registry()

class pin:
def __init__(self, name):
self.name = name

print registry.aap

HTH,
Martin
 
M

Martin Miller

Carl said:
Martin said:
Carl said:
Martin Miller wrote:
### non-redundant example ###
import sys

class Pin:
def __init__(self, name, namespace=None):
self.name = name
if namespace == None:
# default to caller's globals
namespace = sys._getframe(1).f_globals
namespace[name] = self

Pin('aap') # create a Pin object named 'aap'
Pin('aap2') # create a Pin object named 'aap2'
print aap.name
print aap2.name

The problem with this is that it only works for global namespaces,
while failing silently and subtly if used in a local namespace:

Oh, contrair. It would work fine with local namespaces simply by
overriding the default value of the optional 'namepace' parameter (see
below).

Did you try it?

Yes, but I misinterpreted the results which seemed to support my
claim. Therefore I must retract what I wrote and now have to agree
with what you said about it not working in a local namespace --
specifically in the sense that it is unable to bind the instance the
name in the caller's local namespace.

I'm not sure that this is a critical flaw in the sense that it may
not matter for some usages. For example I've seen it used to define
(yet another) Enum class which facilitated the creation of names
bound to a range or sequence of values. The fact that these couldn't
be defined local to code block wasn't apparently a big issue.

Because the usage deceptively suggests that it defines a name in the
local namespace. Failing may be too strong a word, but I've come to
expect a consistent behavior w.r.t. namespaces, which this violates, so
I think it qualifies as a failure.

I don't see how the usage deceptively suggests this at all. In this
case -- your sample code for fun() and fun2() -- all were simply
Pin('aap'). Since no additional namespace argument was supplied, the
same name was bound in the defaulted global namespace each time but
to different objects. In other words the 'print aap1 is aap2'
statement produced 'false' because the call to fun2() changed the
(global) object to which 'aap' was previously bound.
Did you actually try this? ...

As I said, yes, I did, and the addition of the 'locals()' parameter
does make the 'print aap1 is aap2' statement in fun() output 'true'.
This lead me to take for granted that it had bound the name in the
local namespace. However this assumption was incorrect, but that
wasn't obvious since there were no further statements in fun2().

The problem is that there fundamentally doesn't seem to be a way to
create local variables except directly by using an assignment
statement within the block of a function or method. Modifying the
mapping returned from locals() does not accomplish this -- normally
anyway, although interestingly, it will currently if there's an exec
statement anywhere in the function, even a dummy one, but this is
not a documented feature and from what I've read just side-effect of
the way code optimization is be done to support the exec statement.

... Better yet, try this function. Make sure
aap isn't defined as a global:

def fun3():
Pin('aap',locals())
print aap

(I get NameError.)

I do, too, if I run it by itself or first unbind the global left over
from
fun() with a 'del aap' statement.

Did you try it?

Yes -- explained above.

That means it can't be rebound. It doesn't mean you can't modify the
object. Nevertheless, frame objects are an internal feature of Python,
not guaranteed to work for other incarnations of Python (it doesn't
even exist in IronPython, for example), and subject to change. And
what you want to do is easily done in a platform generic way without
using sys._getframe.

Since '_getframe' starts with an underscore, by convention it's
something private, likely an implementation detail, and therefore
not guaranteed not to change in future. Similarily the documentation
says it "should be used for internal and specialized purposes only".

Guess I should have mentioned that this was also an expected
complaint when I warned the OP that some people would oppose the use
of the technique.

My own philosophy is that it's simply a judgment call that depends
on what you're doing and what are the alternatives. Not all code is
mission-critical and even with that which is, it may perhaps be
worth the risk of possible issues later in future in order to have
something now that works in your present environment.

I think programmatically creating variables is fine; I just recommend
you not use sys._getframe, nor the automagical namespace self-insertion
class, to do it.

You've explained some of your worries about sys._getframe. It would
be interesting to hear specifically what it is you don't like about
the idea of namespace self-insertion -- mainly because of the local
namespace limitation?

Carl Banks

Regards,
-Martin
 
C

Carl Banks

Martin said:
Carl Banks wrote:

I don't see how the usage deceptively suggests this at all. In this
case -- your sample code for fun() and fun2() -- all were simply
Pin('aap'). Since no additional namespace argument was supplied,

Exactly. In normal Python, to create a global variable in a local
context, you must specify the namespace. Normally, variables are
always created in the most local context in Python, only. So now you
come in with this this spiffy "look, it creates the variable for you!"
class, but it completely goes against normal Python behavior by
creating the variable in the global namespace even when in a local
context. I think that is deceptive and confusing behvaior for
something that claims to create variables for you.

You've explained some of your worries about sys._getframe. It would
be interesting to hear specifically what it is you don't like about
the idea of namespace self-insertion -- mainly because of the local
namespace limitation?

The local namespace thing has nothing to do with it; I would still be
very much against this even if it did work for locals. My main problem
with this is it saddles the class with behavior that interferes with
using it as a normal class. For instance, if you wanted to do
something like this:

def fun(pin_name):
p = Pin(pin_name)
do_something_with(p)

At least it looks like it works! But it works only with the
undesriable side-effect of creating a global variable of some unknown
name. It could be no big deal, or it could be a gaping security hole.
(Yes, I know you can just pass in an empty namespace. Thanks for that.
Nice to know it's at least possible to jump though hoops just to
regain normal usage of the class.)

The problem here, see, is the hubris of the author in thinking that he
can anticipate all possble uses of a class (or at least that he can
presume that no user will ever want to use the class in a normal way).
Of course, no author can ever anticipate all possible uses of their
code, and it's inevitable that users will want to use code in a way the
author didn't intend. Most often it's not any hubris on the author's
part, but mere ignorance, and is forgivable.

But when the author deliberately does extra work to cut the user off
from normal usage, then ignorance is no longer a defense: the normal
way was considered, and the author sanctimoniously decided on the
user's behalf that the user would never want or need normal usage of
the class. That is a lot less forgivable.

If you want to respect the user, your class is going to have to change.

1. Reprogram the class so that, BY DEFAULT, it works normally, and only
inserts itself into a namespace when specifically requested. Then, at
least, users can ignore the autoinsertion silliness if they don't want
to use it. Plus, it affords better documentation to readers who don't
know aren't in on the secret of this Pin class (seeing an
"autoinsert=True" passed to the constructor is a clue something's going
on).

2. (Better, IMO) Write a well-named function to do it. E.g.:

def create_global_Pin_variable(name,namespace=None):
if namespace is None:
namespace = sys._getframe(1).f_globals
namespace[name] = Pin(name)

Leave it out of the class. Classes are better off when they only worry
about what's going on in their own namespaces. Leave dealing with
external namespaces to an external function. The function's name
documents the fact that: you're creating a variable, it's global, it's
a Pin. You've gotten surprise down to a small as you're going to get
it.


Carl Banks
 
M

Michele Simionato

Martin said:
If you want to, you can combine this with the factory/singleton
patterns, to transparently create the objects on first access:

class Registry:
def __getitem__(self, name):
# only invoked when attribute is not set
r = pin(name)
setattr(self, name, r)
return r
registry = Registry()

class pin:
def __init__(self, name):
self.name = name

print registry.aap

<nitpick>
You probably meant __getattr__ instead of __getitem__ here ;)
</niptick>

This is an interesting approach, but I would probabily use the
traditional registry you discussed
before (too much magic here).

Michele Simionato
 

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

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,053
Latest member
BrodieSola

Latest Threads

Top