7

I am trying to achieve the following:

class A:
    username = None
    username = get_username()
    def get_username(self):
        if username is None:
            try:
                uname = os.environ["USER"]
            except:
                printf("Couldn't find a user name")
            return uname
        return username

Not sure how to achieve this. I'm sure I'm missing some "self." prefixes but this is the first time I'm working with python and static members.

In a sense I want a class with some members and functions to calculate values for these members but I don't want recalculations. I would also like these to be static functions and data members.

The problem is that the line "username = get_username()" the function hasn't already been defined. If I put username after the function then it's not

s5s
  • 11,159
  • 21
  • 74
  • 121
  • From what I gather, you want lazy attribute generation. See this question for how to do that nicely with a decorator: http://stackoverflow.com/questions/3012421/python-lazy-property-decorator – Gareth Latty Nov 02 '12 at 19:35
  • 2
    Also note that ``except`` without defining a specific exception to catch is horribly bad practice. – Gareth Latty Nov 02 '12 at 19:38
  • When you say "static members," I think you mean attributes that belong to the class, not to individual instances of the class. Is that right? – senderle Nov 02 '12 at 19:39
  • Yes that's right. That's what static means in C/C++/Java/.Net etc. – s5s Nov 02 '12 at 19:46
  • 1
    I know. I'm just asking because at least in my experience, it's not very common to refer to class attributes as "static" in Python (the `@staticmethod` decorator notwithstanding). I believe that's why Lattyware thought this was a duplicate -- as did I, initially. – senderle Nov 02 '12 at 19:48
  • It's actually incorrect to refer to those things as static in Python. C++'s static members and static methods are called class members and class methods in Python (partly because Python is a dynamic language, and the methods actually get passed the class object itself). What Python calls static methods are methods that don't even have access to even the class members. – abarnert Nov 02 '12 at 21:57

2 Answers2

13

First, there's no reason to assign None to username if you're just going to reassign it immediately after.

Second, if you want the method to be a static method, you can't give it a self argument. And if you want a real static method, you have to declare it explicitly.

    @staticmethod
    def get_username():
        if username is None:
        ...

Otherwise, you need an instance of the class (that self) to call it on, and you don't have one yet.

In Python 3, any regular method acts like a static method when called on the class, like an instance method when called on an instance. So, if you're sure you're never going to want to call a.get_username() on an instance a, you can skip the decorator. But you still need to get rid of the self parameter.

I think what you're actually trying to do is use a class variable to memoize the result of a static method. You can't do that, but you can use a class variable to memoize the result of a class method, which may be close enough. That would look like this:

class A:
    username = None
    @classmethod
    def get_username(cls):
        if cls.username is None:
            try:
                uname = os.environ["USER"]
            except:
                print("Couldn't find a user name")
            else:
                cls.username = uname
        return cls.username

On the other hand, there's no good reason username has to be a class member. You can memoize by adding a member to the function, by passing a mutable default variable, or in various other ways which don't require infecting the class, and which allow you to leave get_username as a static method instead of a class method.

But really, the best solution is to find a memoization library on PyPI, in ActiveState's recipe list, etc., so you can just write this:

class A:
    @memoize
    @staticmethod
    def get_username():
        try:
            return os.environ["USER"]
        except:
            print("Couldn't find a user name")
            return None

Again, you can drop the @staticmethod if you're sure nobody's ever going to try to create an instance of A and call get_username on it.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • There's a change between python3 and Python2 that I don't think you know about. In python2 the following code breaks, in python3 it prints 'win': (excuse the stupid formatting, I hope this makes sense... each new line I'll put in []. also _ is a space. damn, this sucks a little) [`class A:`][`____def win():`][`________print('win')`][``][`A.win()`] – Sheena Nov 02 '12 at 20:12
  • I understand what you're saying. I thought explaining how regular methods can act like implicit static methods in Py3 would make things more complicated to no real benefit, but I've edited the answer to include as much explanation as possible without getting too much in the way (I hope). – abarnert Nov 02 '12 at 21:54
1

if you don't want to lose the ability to refer to A.username, you can use class property with a little bit of metaclass:

class A(type):
    def __new__(cls, name, bases, attrs):
        # this allows B().username to also work
        attrs['username'] = property(lambda s: s.__class__.username)
        return type.__new__(cls, name, bases, attrs)
    @property
    def username(self):
        if not hasattr(self, '_username'):
            self._username = 'bar'
        return self._username

class B(object):
    __metaclass__ = A

print B.username
print B().username
Lie Ryan
  • 62,238
  • 13
  • 100
  • 144