2

Not sure if this is possible in Python, but I'd like to have an instance of a class as a module-level global variable that, when called, returns a default property.

The idea comes from this question that overrides __call__ to have a class mimic a function.

Quick (untested) example of the idea:

class _foo(object):
    __slots__ = ("_foo",)
    _init = False

    def __init__(self):
        if not _init:
            self._foo = ["bar", "baz", "zog"]

    def __call__(self, item=None):
        if itme is not None:
            return self._foo[item]
        else:
            return self._foo

    def __getitem__(self, item):
        return self._foo[item]

# Module level
FOO = _foo()



Assume the above code is in a module (foo.py). Using the interpreter, I cal get the callable form to work how I want, but not the non-callable form.

>>> import foo

# callable forms
>>> foo.FOO(0)
"bar"
>>> foo.FOO()
["bar", "baz", "zog"]

# attribute forms
>>> foo.FOO[2]
"zog"

# doesn't work
>>> foo.FOO
<foo._foo object at 0xdeadbeef>

# desired output
>>> foo.FOO
["bar", "baz", "zog"]



My thinking is that I need to define a default property somehow so that when calling the instance directly, I get the internal list/dict of my choosing. I could cheat and do this via overriding repr, but that kinda breaks Python's own API and I want to avoid that. Passing no value in square brackets (>>> foo.FOO[]) yields a syntax error instead.

I'd like any potential solutions to be compatible with Python 2.4 at minimum and 2.7 at maximum. If this isn't possible, then I guess I'll just stick to using the callable format.

Community
  • 1
  • 1
Kumba
  • 2,390
  • 3
  • 33
  • 60

3 Answers3

3

This is impossible, sorry. You can't have foo.FOO be both a callable (a _foo instance) and an unadulterated list object.

Of course, I did say "unadulterated". If you're feeling sneaky...

class _foo(list):
    _init = False
    def __init__(self):
        if self._init:
            list.__init__(self)
        else:
            list.__init__(self, ['bar', 'baz', 'zog'])

    def __call__(self, item=None):
        if item is None:
            return self
        return self[item]

FOO = _foo()

Usage:

>>> FOO()
['bar', 'baz', 'zog']
>>> FOO(0)
'bar'
>>> FOO
['bar', 'baz', 'zog']
nneonneo
  • 171,345
  • 36
  • 312
  • 383
  • I don't want it to be both, no. In a sense, FOO is a variable that stores an instance of `class _foo`, such that if I go `FOO(), I am really invoking the `__call__()` method and returning a list object. What I would like to do instead is have a default property/attribute for `_foo` such that if it is directly referenced, instead of getting the address of the instance returned, some default handler returns the list instead. IF that makes any sense. – Kumba Sep 18 '12 at 02:32
  • You could do this in VB6, for example, by designation a specific property of a class as the class' default property. Except in Python, I want to do this on a variable holding an instance of that class. Overriding `__call__()` accomplishes this, but leads to odd-looking syntax like `x = foo.FOO()` to return the list, when instead I want `x = foo.FOO` and get the list instead. It's really a style issue at heart, but I am curious if it can be done or not. – Kumba Sep 18 '12 at 02:34
  • In Python, if you say `foo.FOO`, you get the `FOO` attribute (member) of the `foo` module (in general object), which in your case is always the `_foo` instance. This applies in any context. Accessing an attribute runs `__getattr__` on the parent object (in this case `foo`, but will never run anything on the attribute itself. You should mention you're coming from VB so that clears up the confusion. – nneonneo Sep 18 '12 at 02:50
  • I haven't worked in VB6 in a few years. I just cited it as an example of a language that allows you to mark a specific property as a class' default. I'd have gone into more detail, but these SO comment boxes don't allow line feeds and I only have 500 chars to type in. I'll probably just stick with the callable form once I work out this infinite recursion issue. – Kumba Sep 18 '12 at 03:12
  • Interesting. Parameterless default properties in VB6, if I understand them correctly, permit the use of a syntactic sugar which allows you to treat an object as if it were one of its properties. You can emulate much of the same functionality in Python by exploiting duck typing -- since most interfaces in Python don't do explicit type checks (preferring instead to rely on the object having certain attributes), if you expose the properties (attributes/methods) of the 'default property' as properties of your object, you get much the same effect as PDP. – nneonneo Sep 18 '12 at 03:18
  • Yup. It's what allows you, in VB6, to do `TextBox1 = "FOO"` or `debug.print TextBox1` and set/get the `.Text` property because that is the default property for a TextBox object. In Python, I'm working with the [dpkt](http://code.google.com/p/dpkt/) library, which has a clever routine in `dpkt.ethernet.Ethernet` to load a dict that stores a copy of objects of each ethertype protocol (IP, IPX, ARP, etc), based on a definition of `ETH_TYPE_*` at the top. Problem is, in `dpkt.gre`, he tries to copy ethernet's `typesw` variable and in certain situations, this can lead to infinite recursion. – Kumba Sep 18 '12 at 03:35
  • So I am trying to solve that AND remove redundant code by moving the initializer code to its own module, while retaining the `ETH_TYPE_*` definitions within their respective modules. But I am still running into the infinite recursion issue. Only solution right now is to duplicate all the supported `ETH_TYPE_*` definitions for the `dpkt.gre` module to break the recursion. – Kumba Sep 18 '12 at 03:37
  • Have you ever *seen* it infinite recurse? That might be a bug...the `import` is at the bottom for a reason. – nneonneo Sep 18 '12 at 03:38
  • That's where this question derived from. I am using the static class idea referenced in [this question](http://stackoverflow.com/q/460586) and just wanted to find a way to reference the class instance directly and get the types dict back (instead of the object reference) by having a default property. Instead, I think I'm just going to stick with the callable form and find a solution to the infinite recursion before I send the code back upstream. – Kumba Sep 18 '12 at 03:40
  • Yup. In fact, I am adding new modules, such as LLC for IEEE 802.2 framing after IEEE 802.3 (not EthII). This works fine on its own, but once I removed the redundant code from `dpkt.ethernet.Ethernet` and replaced it with a reference to `dpkt.llc.LLC`, I tripped the recursion up. If I solve the recursion, I can reference the LLC module and get proper layer separation between each protocol. – Kumba Sep 18 '12 at 03:43
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/16802/discussion-between-nneonneo-and-kumba) – nneonneo Sep 18 '12 at 03:44
1

I'm not sure what you mean by Right now, when I try this within another module, I get an AttirbuteError that FOO doesn't exist within the module, this code that imports your module works correctly for me

import foo
FOO2 = foo.FOO
print(FOO2())
print(FOO2(1))

Python 2.7.3

fox
  • 71
  • 3
  • 3
  • It's possible Python 2.7 does things better. I only have access to an antiquated Python 2.4 environment right now, so I may be fighting some quirk present in 2.4 that was addressed in newer releases. I wrote the example based off existing code, and that existing code is not working correctly. Going to open another question on it in a few minutes. – Kumba Sep 18 '12 at 01:16
  • It appears part of my issue with this is a case of infinite recursive module loading. Not the original author of the library I am working in, so I have to figure out a cleaner way of doing what that library is trying to do. – Kumba Sep 18 '12 at 02:48
0

Define __repr__(self) on your class to define how its instances appear in the Python inteprreter. In this case, perhaps return repr(self._foo).

kindall
  • 178,883
  • 35
  • 278
  • 309
  • The interpreter example is just for reference to support the question. The goal is to have other modules be able to `import foo` then do something like `x = foo.FOO` and x receives a copy of the foo.FOO instance (copy or pointer, whatever works). Right now, when I try this within another module, I get an AttirbuteError that `FOO` doesn't exist within the module, despite the example working fine in the interpreter. – Kumba Sep 18 '12 at 00:34
  • There'sno problem making foo.FOO be an instance of some type and also making this type callable. I guess I'm not clear on what you're trying to do. – kindall Sep 18 '12 at 02:01