Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python beginner Class variable Error

this is my first question, so sorry... I'm a beginner with python and coding in general, and i wanted to create a class called 'Map' that would have the following class variables:

class Map:
    height = 11  
    width = 21

    top = [['#']*width]
    middle = [['#']+[' ']*(width-2)+['#'] for i in range(height-2)]
    field = top + middle + top

b = Map()

Shell:
>>> middle = [['#']+[' ']*(width-2)+['#'] for i in range(height-2)]
    NameError: name 'width' is not defined

If i put the variables outside of the class it works. What am I doing wrong??

Thanks for the help.

like image 634
Cookiesforhelp Avatar asked May 25 '15 19:05

Cookiesforhelp


1 Answers

From the docs:

Names refer to objects. Names are introduced by name binding operations. Each occurrence of a name in the program text refers to the binding of that name established in the innermost function block containing the use.

A block is a piece of Python program text that is executed as a unit. The following are blocks: a module, a function body, and a class definition. Each command typed interactively is a block. A script file (a file given as standard input to the interpreter or specified as a command line argument to the interpreter) is a code block. A script command (a command specified on the interpreter command line with the ‘-c‘ option) is a code block. The string argument passed to the built-in functions eval() and exec() is a code block.

A code block is executed in an execution frame. A frame contains some administrative information (used for debugging) and determines where and how execution continues after the code block’s execution has completed.

A scope defines the visibility of a name within a block. If a local variable is defined in a block, its scope includes that block. If the definition occurs in a function block, the scope extends to any blocks contained within the defining one, unless a contained block introduces a different binding for the name. The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods – this includes comprehensions and generator expressions since they are implemented using a function scope. This means that the following will fail:

class A:
    a = 42
    b = list(a + i for i in range(10))

list comps in python3 have their own scope, as opposed to python2 where your code would work as is.

If you take the following example using python2, you can see how variables leaking the scope of the list comp could cause some problems:

class A:
    a = 42
    b = [a for a in range(10)]

a = A()

print(a.a)
9

You have a few options, you could use __init__ creating instance attributes:

class Map:
    def __init__(self):
        self.height = 11
        self.width = 21
        self.top = [['#']*self.width]
        self.middle = [['#']+[' ']*(self.width-2)+['#'] for i in range(self.height-2)]
        self.field = self.top + self.middle + self.top
m=Map()
print(m.field)

Using a method:

class Map:
    @staticmethod
    def meth():
        height = 11
        width = 21
        top = [['#']*width]
        middle = [['#']+[' ']*(width-2)+['#'] for i in range(height-2)]
        field = top + middle + top
        return field

b = Map()


print(b.meth())

What you choose is really dependant on what you want to do.

like image 175
Padraic Cunningham Avatar answered Nov 12 '22 19:11

Padraic Cunningham