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

Discussion in 'Python' started by Stef Mientki, Jan 5, 2007.

  1. Stef Mientki

    Stef Mientki Guest

    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
    Stef Mientki, Jan 5, 2007
    #1
    1. Advertising

  2. Stef Mientki

    Laszlo Nagy Guest

    Stef Mientki wrote:
    > 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
    Laszlo Nagy, Jan 5, 2007
    #2
    1. Advertising

  3. Stef Mientki

    Fuzzyman Guest

    Stef Mientki wrote:
    > 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

    > thanks,
    > Stef Mientki
    Fuzzyman, Jan 5, 2007
    #3
  4. Stef Mientki

    Jorge Vargas Guest

    On 1/5/07, Stef Mientki <> wrote:

    if I undestand correctly this is what you want

    >>> class pin3:

    .... def __init__(self,name):
    .... self.Name = name
    .... def __str__(self):
    .... return self.Name
    ....
    >>> pin3()

    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    TypeError: __init__() takes exactly 2 arguments (1 given)
    >>> pin3('aaa')

    <__main__.pin3 instance at 0xb7dc948c>
    >>> print pin3('aaa')

    aaa
    Jorge Vargas, Jan 5, 2007
    #4
  5. Stef Mientki

    Carl Banks Guest

    Stef Mientki wrote:
    > 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
    Carl Banks, Jan 5, 2007
    #5
  6. Stef Mientki wrote:
    > 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
    Martin Miller, Jan 6, 2007
    #6
  7. Stef Mientki

    Carl Banks Guest

    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:

    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
    Carl Banks, Jan 6, 2007
    #7
  8. Stef Mientki wrote:
    > 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::

    >>> class pin(object):

    ... 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::

    >>> class aap:

    ... __metaclass__ = pin.from_class_block
    ...
    >>> print aap.name

    aap
    >>> type(aap)

    <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
    Steven Bethard, Jan 6, 2007
    #8
  9. Stef Mientki

    Stef Mientki Guest

    Steven Bethard wrote:
    > Stef Mientki wrote:
    >> 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
    Stef Mientki, Jan 6, 2007
    #9
  10. Carl Banks wrote:

    > 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).

    > 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
    Martin Miller, Jan 7, 2007
    #10
  11. Re:[OT] (newbie) Is there a way to prevent "name redundancy" in OOP?

    Martin Miller a écrit :
    (snip)
    >
    > Oh, contrair.


    I guess you mean "au contraire" ?-)

    (snip)
    Bruno Desthuilliers, Jan 7, 2007
    #11
  12. Re: Re:[OT] (newbie) Is there a way to prevent "name redundancy" in OOP ?

    Bruno Desthuilliers wrote:

    > Martin Miller a écrit :
    > (snip)
    > >
    > > Oh, contrair.

    >
    > 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
    Martin Miller, Jan 7, 2007
    #12
  13. Re: [OT] (newbie) Is there a way to prevent "name redundancy" inOOP ?

    Martin Miller a écrit :
    > Bruno Desthuilliers wrote:
    >
    >>Martin Miller a écrit :
    >>(snip)
    >>
    >>>Oh, contrair.

    >>
    >>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".


    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.
    Bruno Desthuilliers, Jan 7, 2007
    #13
  14. Stef Mientki

    Carl Banks Guest

    Martin Miller wrote:
    > Carl Banks wrote:
    >
    > > 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?

    > > 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:


    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
    Carl Banks, Jan 7, 2007
    #14
  15. Stef Mientki schrieb:
    > 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
    =?ISO-8859-1?Q?=22Martin_v=2E_L=F6wis=22?=, Jan 8, 2007
    #15
  16. Carl Banks wrote:
    > Martin Miller wrote:
    > > Carl Banks wrote:
    > >
    > > > 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.


    > > > 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:

    >
    > 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.

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

    >
    > 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.


    > > 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?


    Yes -- explained above.


    > > 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.


    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 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.


    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?


    > > 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


    Regards,
    -Martin
    Martin Miller, Jan 10, 2007
    #16
  17. Stef Mientki

    Carl Banks Guest

    Martin Miller wrote:
    > Carl Banks wrote:


    > > 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,


    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.


    > > 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?


    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
    Carl Banks, Jan 10, 2007
    #17
  18. Martin v. Löwis wrote:
    > 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
    Michele Simionato, Jan 11, 2007
    #18
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Bruce Sam

    A newbie in OOP program

    Bruce Sam, Jan 4, 2005, in forum: Java
    Replies:
    3
    Views:
    320
    dingo
    Jan 4, 2005
  2. Larry Lindsey

    Newbie: OOP reference

    Larry Lindsey, Sep 15, 2003, in forum: C++
    Replies:
    8
    Views:
    346
    White Wolf
    Sep 15, 2003
  3. Ed

    Newbie OOP question

    Ed, Nov 18, 2003, in forum: Python
    Replies:
    3
    Views:
    295
    Lawrence Oluyede
    Nov 18, 2003
  4. LenS

    Newbie learning OOP

    LenS, May 29, 2005, in forum: Python
    Replies:
    5
    Views:
    325
    Terry Hancock
    Jun 1, 2005
  5. Replies:
    4
    Views:
    177
    Joe Smith
    May 4, 2007
Loading...

Share This Page