tags:

views:

93

answers:

4

The below code works both under Python 2.6 and 3.1, but the third lambda of SomeObject.columns is a bit silly, serving no real purpose but to prevent the reference to SomeObject.helper_function from being looked at before the class declaration finishes. It seems like a hack. If I remove the lambda, and replace it with just SomeObject.helper_function, I get NameError: name 'SomeObject' is not defined. Am I missing a better non-hacky way?

class SomeObject:
  def __init__(self, values):
    self.values = values

  @staticmethod
  def helper_function(row):
    # do something fancy here
    return str(len(row))

  columns = [
    (lambda x: x['type'], 'Type'),
    (lambda x: 'http://localhost/view?id=%s' % x['id'], 'Link'),
    (lambda x: SomeObject.helper_function(x), 'Data'),
    ]

  def render_table_head(self):
    print('\t'.join([c[1] for c in self.columns]))

  def render_table_body(self):
    for row in self.values:
      print('\t'.join([col[0](row) for col in self.columns]))
+2  A: 

There's no way to refer to the class that's currently being defined. There should really be keywords referring to the current scope, eg. __this_class__ for the innermost class being defined and __this_func__ for the innermost function, so classes and functions can cleanly refer to themselves without having to repeat their name.

You could move the definition of columns out of the class body:

class SomeObject:
    def __init__(self, values):
        self.values = values
    ...

SomeObject.columns = [
    (lambda x: x['type'], 'Type'),
    (lambda x: 'http://localhost/view?id=%s' % x['id'], 'Link'),
    (SomeObject.helper_function, 'Data'),
]

By the way, please always use at least 4-space indentation. Anything less is very hard to read.

Glenn Maynard
FWIW, I've never had problems referring to the function being defined by name. The code doesn't actually run until the name is defined. Am I missing something?
aaronasterling
@Aaron: That doesn't give you the function itself, it gives you whatever happens to be bound to that name; for example, that's different if the function is decorated, or if the containing scope changes it for some other reason. *Generally* that's what you want anyway, but I think that you should be able to get a reference to "this object" in the general case, without having to "look yourself up" by name.
Glenn Maynard
@Glenn: Yeah, I've noticed the lack of those kinds of keywords before, but I'd assumed my desire for them was a product of C/C++ tainted mind, and managed to find perfectly good, elegant alternatives. This is the first time it seemed like there weren't any.
John Dough
A: 

Why not populate columns in init() and use self?

def __init__(self, values):
    self.values = values
    self.columns = [
        (lambda x: x['type'], 'Type'),
        (lambda x: 'http://localhost/view?id=%s' % x['id'], 'Link'),
        (self.helper_function, 'Data'),
    ]
Paulo Scardine
Those are instance variables, not class variables.
Glenn Maynard
A: 

This works. It goes against all of my sensibilities.

class SomeObject:
  def __init__(self, values):
    self.values = values

  def helper_function(row):
    # do something fancy here
    return str(len(row))

  columns = [
    (lambda x: x['type'], 'Type'),
    (lambda x: 'http://localhost/view?id=%s' % x['id'], 'Link'),
    (helper_function, 'Data'),
    ]

  def render_table_head(self):
    print('\t'.join([c[1] for c in self.columns]))

  def render_table_body(self):
    for row in self.values:
      print('\t'.join([col[0](row) for col in self.columns]))


if __name__ == '__main__':
    print "foo"

    o = SomeObject([{'type':'type100', 'id':'myId'}, {'type':'type200', 'id':'myId2'}])
    o.render_table_body()
kevpie
How did you think of that? Its perverse, but in a very subtle way. Its surprising to me that having a self-less classmethod can actually work out!
John Dough
I threw your code into pycharm and just started trying stuff. At first I wanted to reproduce your findings. Then I was trying to avoid putting the code into __init__. And I concur, it is perverse!
kevpie
A: 

You can directly refer to the static function through

(helper_function.__func__, 'Data'),

without having to change anything else in your code. helper_function is of type staticmethod, and __func__ gives access to the underlying function.

EOL