0
class class_name:
    def foo1(self):
        print "foo1"

    def foo2(self):
        print "foo2"

    def foo(self, key):
        self.dictionary[key]()

dictionary = {
'key1' : class_name.foo1,
'key2' : class_name.foo2
}

>>>class_name().foo('key1')

Error: AttributeError: class_name instance has no attribute 'dictionary'

Although I understand the error, I do not know how to rectify it.

Even if I define the dictionary inside the class "by indenting it to be inside the class definition, it then generates the error that NameError: name 'class_name' is not defined"*

Any advice on how to fix this is appreciated.

EDIT: I need a single dictionary for the class, not one dictionary per object instance

A little insight into the real problem: I am making a chess game. So while defining the game logic, each piece has its own style of moving. I wished to call a single function to give me the legal moves as per the current state of the chess board. This single function (in the example foo()) has to call other functions based on the type of the piece and also provide those functions with the object instance (and other parameters), so that they can compute the moves. The problem I face is to pass the object instance to the piece specific functions (foo1() and foo2()).

Maybe this will help. This is what I want to achieve in the function foo():

switch(key){
    case 'key1':
        self.foo1();
        break;
    case 'key2':
        self.foo2();
        break;
}

* added from comment

tmj
  • 1,750
  • 2
  • 19
  • 34
  • How are you defining the dictionary within the class? – Jon Clements Sep 25 '13 at 10:56
  • just by indenting it to be inside the class definition, it then generates the error that `NameError: name 'class_name' is not defined` – tmj Sep 25 '13 at 10:58
  • If you want to access the class's built-in dict, it's `self.__dict__` – number5 Sep 25 '13 at 11:02
  • WHy do you want a class that contains a dictionary that points to class functions, which clearly would then need to be static? What are you really trying to do? – doctorlove Sep 25 '13 at 11:08
  • I still don't understand the problem, and that would solve it – Paco Sep 25 '13 at 11:17
  • 1
    I think the problem is that the code is not working. And that, that I am having a hard time comprehending how to make it work, and in the right manner – tmj Sep 25 '13 at 11:19

4 Answers4

1

The problem you have when defining dictionary outside of the class is that the class doesn't have dictionary as attribute. You could fix this doing:

class_name.dictionary = {'foo1': class_name.foo1, 'foo2': class_name.foo2}

after the class definition.

The error you see when defining dictionary inside the class is due to the fact that you cannot reference the class inside the class definition, since when that code is executed the class doesn't exist.

Something you have to rememeber is that python doesn't have any kind of "declaration" statement. All statement execute code. Hence, the body of the class is run by the interpreter inside a new scope and afterwards it sets the created scope as the __dict__ of the class object.

Since the class definition is executed you can reference the foo1 and foo2 functions directly from that scope:

In [9]: class A(object):
   ...:     def foo1(self):
   ...:         print('foo1')
   ...:     def foo2(self):
   ...:         print('foo2')
   ...:     def foo(self, key):
   ...:         self.dictionary[key](self)
   ...:     dictionary = {'foo1': foo1, 'foo2': foo2}  # NOTE: do NOT use cls_name.method

In [10]: a = A()

In [11]: a.foo('foo1')
foo1

In [12]: a.foo('foo2')
foo2

However note that inside the foo method(even with the first solution) you must also manually pass the self parameter.

This happens because when creating the dictionary you are assigning the functions(or unbound-methods in python2) foo1 and foo2, not the instance methods, hence those values do not have information on the instance they are called on.


I agree with the other answer, that this kind of pattern isn't really useful since you could use getattr to achieve the same functionality without having to use dicts at all. Simply do getattr(instance, method_name)().

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
  • aaah! would I have to supply self manually like that ? Is there no other means? – tmj Sep 25 '13 at 11:32
  • @tMJ Not if you want to have a "class-level" `dictionary`. In python methods are implemented as descriptors, i.e. when you do `method = instance.foo1` you obtain an instance of a *bound method*, but if you do `class_name.foo1` you get an instance of a *function*(in python3, an unbound method in python2, which you can think of as a function anyway). In the first case the `self` parameter is passed automatically, in the latter it isn't. – Bakuriu Sep 25 '13 at 11:39
  • does this mean I can call any function inside the class not just by `object_instance.method_name()` but also by `method_name(object_instance)` ? – tmj Sep 25 '13 at 11:44
  • @tMJ What do you mean by "inside the class"? – Bakuriu Sep 25 '13 at 11:47
  • You mean to do things like `def method(self): other_method(self)` instead of `def method(self): self.other_method()`? It wont work because when executing `method` python will *not* look for class attributes, hence it wont be able to find it. If you mean to call `method` on an instance of the class, then it will not work anyway for the same reason, *except* if you have a global variable named `other_method` that contains a reference to `class_object.other_method`. – Bakuriu Sep 25 '13 at 12:07
1

Instead of storing functions in the dictionary, you can store function names and use getattr to access them.

class class_name:
    dictionary = {
    'key1' : 'foo1',
    'key2' : 'foo2'
    }

    def foo1(self):
        print "foo1"

    def foo2(self):
        print "foo2"

    def foo(self, key):
        getattr(self, self.dictionary[key])()

You can now do this

>>> class_name().foo('key1')
foo1

The dictionary is the same for all instances.

>>> c1 = class_name()
>>> c2 = class_name()
>>> c2.dictionary['key3'] = 'foo1'
>>> c1.foo('key3')
foo1

That said, I'm not quite sure what you want to achieve and if that is the right approach.

Why are the piece specific function not part of a piece base class and overwritten in the different specific piece classes? Something like this

class class_name:
    def foo(self, piece):
        print piece.get_valid_moves()

class BasePiece:
    def get_valid_moves():
        raise NotImplementedError

class Queen:
    def get_valid_moves():
        return ['diagonal', 'vertical', 'horizontal']

class Tower:
    def get_valid_moves():
        return ['vertical', 'horizontal']
Daniel Hepper
  • 28,981
  • 10
  • 72
  • 75
  • well I dont have a separate class for each piece because I didn't need the functionality making each piece a object of its own. Thanks anyway! – tmj Sep 25 '13 at 11:41
0

You have defined dictionary outside your class, so self.dictionary makes no sense.
If you want to have a dictionary in each class instance, set it in __init__ See here for details

Try this

class class_name:
    def foo1(self):
        print "foo1"

    def foo2(self):
        print "foo2"

    def foo(self, key):
        self.dictionary[key]()

    def __init__(self):
        self.dictionary = {
        'key1' : self.foo1,
        'key2' : self.foo2
        }

Edit If you want all instances of class_name to use the same foo1 and foo2 in the same dictionary, and the methods are instance methods, from which instance do you intend to use? Or should the foo1 and foo2 not be instance methods? Why are you even using a class in that case?

Community
  • 1
  • 1
doctorlove
  • 18,872
  • 2
  • 46
  • 62
0

You either need to create one object, and pass in this object to the dictionary such as:

my_object = class_name()
dictionary = {
    'key1' : my_object.foo1,
    'key2' : my_object.foo2
}

Or create static methods:

class class_name:
    @staticmethod
    def foo1():
        print "foo1"

    @staticmethod
    def foo2():
        print "foo2"

    def foo(self, key):
        self.dictionary[key]()  # This doesn't make sense, your class doesn't have an attribute called dictionnary

dictionary = {
    'key1' : class_name.foo1,
    'key2' : class_name.foo2
}

EDIT: You do not need a dictionnary for this kind of stuff. Use getattr

Something like:

method = gettatr(my_current_object, 'method_name')
method()
Paco
  • 4,520
  • 3
  • 29
  • 53
  • I do not understand, `create one object, and pass in this object to the dictionary` Will that work with multiple object instances ? Or will I have to make separate dictionaries for each object, because I wouldn't wanna do that. – tmj Sep 25 '13 at 11:03
  • 1
    On dictionary points to one object in that case. Can you give more information about what you are trying to achieve? It's hard to tell what you want exactly – Paco Sep 25 '13 at 11:04
  • Note that when using `@staticmethod` you should *not* add the `self` parameter, since it wont be passed. As is your code will raise a `TypeError` for missing argument. – Bakuriu Sep 25 '13 at 11:25
  • I have updated my answer, I don't think you need a dictionary for that, getattr will do – Paco Sep 25 '13 at 11:32