简体   繁体   中英

What's the difference between an inner class and an inner inner class in python? [duplicate]

Shouldn't field be undefined on line 50? It was my understanding that inner nested classes did not have visibility to outer classes, as I ran into on line 65... Just seems kind of inconsistent and I would love to have a better understanding of the outer nested classes visibility to avoid any confusion in the future.

"""######################################### Imports ############################################"""
import turtle
import time
import functools
"""######################################### Key press ##########################################"""

def key_space():  # Resets Level
    field.setLevel()

def key_p():      # Skips Level
    field.nextLevel()

def key_k():      # For making levels
    print([(b.x,b.y) for b in field.getBoxes()])
    
"""######################################## Interface(s) ########################################"""

def stamp(self):  # Interface for drawing polygons.
    turtle.goto(self.canvasX+field.offset[0],self.canvasY+field.offset[1])
    turtle.shape(self.shape)
    turtle.color(self.color)
    turtle.stamp()

def frameBoiler(func): #
    @functools.wraps(func)
    def wrapper_frame_boiler(*args,**kwargs):
        turtle.clear()
        #turtle.getscreen().bgpic('Untitled.png')
        r = func(*args, **kwargs)
        turtle.update()
        time.sleep(0.05)
        return r
    return wrapper_frame_boiler

"""######################################### Game Logic #########################################"""

class field:      # Field is an abstract class which facilitates the game Mechanics
    def place(o): # Place translates an objects board position to a pixel or canvas position
        o.canvasX = (o.x-31)*field.spaceSize # 31 beacuse it's half of the magic number...
        o.canvasY = (o.y-31)*field.spaceSize
        return(o) # This method should be a void //TODO refactor...
    class square:
        padding = 2
        color = "#078900"
        shape = "sq" # "sq" is the name of the stamp polygon geometry.
        #//TODO refactor: eliminate redundant literals. see def turtleSetup
        def __init__(self,x,y):
            self.x = x
            self.y = y
            field.place(self)
        def draw(self):
            stamp(self)
    class level:  # Levels are what make the game fun.
        class mando: # These are the spots you have to populate
            thickness = 2 # 
            trim = 1
            color = "#ee0000"
            shape = "bx" # "bx" is the name of the stamp polygon geometry. 
            #//TODO refactor: eliminate redundant literals. see def turtleSetup
            def draw(self): # each mando needs to be visable...
                stamp(self) # line: 15: `def stamp(self)`
            def __init__(self,x,y): # make a mandatory space
                self.x = x
                self.y = y
                self.canvasX = 0 # field isn't visible from this deeply nested class
                self.canvasY = 0 # so these are "unplaced"
        def __init__(self,text,spots): # make a level
            self.text = text
            self.mandos = []
            self.complete = False
            for spot in spots:
                self.mandos.append(self.mando(spot[0],spot[1]))
        def checkWin(self): # If the boxes on the board match the mandos you beat the level.
            oneLiner = set([(b.x,b.y) for b in field.getBoxes()])
            isTooLong = set([(m.x,m.y) for m in self.mandos])
            self.complete = oneLiner == isTooLong
        def draw(self): # This draws the mandos and the level's acompanying text.
            for mando in self.mandos:
                mando.draw()
            if self.complete:
                field.nextLevel()
    spaces = [[None]*63 for k in range(0,63)] # 63 is the magic number, ralistically it could be 8
    levels =[level("letsGo",[(31,33),(31,29),(29,31),(33,31)]),
            level("Never\nEat\nShreaded\nWheat",[(27, 31), (28, 30), (28, 32), (29, 30), (29, 32),
            (30, 28), (30, 29), (30, 33), (30, 34), (31, 27), (31, 35), (32, 28), (32, 29),
            (32, 33), (32, 34), (33, 30), (33, 32), (34, 30), (34, 32), (35, 31)]),
            level("Try:\nPress Space",[(29, 31), (30, 30), (31, 31), (31, 33), (32, 29), (33, 32),
            (34, 31)]),
            level("Flex",[(28, 27), (28, 28), (28, 29), (29, 26), (29, 30), (29, 35), (29, 36),
            (30, 29), (30, 31), (30, 33), (30, 34), (30, 35), (30, 37), (31, 28), (31, 29),
            (31, 33), (31, 35), (31, 36), (32, 29), (32, 31), (32, 33), (32, 34), (32, 35),
            (32, 37), (33, 26), (33, 30), (33, 35), (33, 36), (34, 27), (34, 28), (34, 29)]),
            level("Blast Off",[(28, 28), (28, 29), (28, 31), (29, 27), (29, 31), (29, 32), (30, 28),
            (30, 29), (30, 31), (30, 32), (30, 33), (30, 34), (31, 33), (31, 35), (32, 28), 
            (32, 29), (32, 31), (32, 32), (32, 33), (32, 34), (33, 27), (33, 31), (33, 32),
            (34, 28), (34, 29), (34, 31)]),
            level("Space\nInvaders",[(27, 31), (28, 30), (28, 32), (29, 27), (29, 28), (29, 30),
            (29, 31), (30, 26), (30, 28), (30, 29), (30, 33), (31, 27), (31, 28), (31, 31),
            (31, 32), (32, 26), (32, 28), (32, 29), (32, 33), (33, 27), (33, 28), (33, 30),
            (33, 31), (34, 30), (34, 32), (35, 31)]),
            level("big oof",[(31,31),(32,31),(31,33)])]
    levelIndex = 0   # literally the number indicating what level you're on
    spaceSize = 40   # the number of pixels to a in gmae space.
    offset = [-80,0] # you can arbitrailly move the gmae around the screen...
    def hit(x,y):    # toggle the presence of a box in a space on the board/field.
        try:
            if field.spaces[x][y] is None:
                field.spaces[x][y] = field.place(field.square(x,y))
            else:
                field.spaces[x][y] = None
        except IndexError:
            pass
    def setLevel():  # clears the board puts a box in the middle and places the mandos.
        field.spaces = [[None]*63 for k in range(0,63)]
        field.hit(31,31)
        [field.place(mando) for mando in field.levels[field.levelIndex].mandos]
    def nextLevel(): # the first level is also the level after the last level.
        field.levelIndex += 1
        if field.levelIndex >= len(field.levels):
            field.levelIndex = 0
        field.setLevel()
    @frameBoiler
    def draw():      # this is the draw method for the 
        field.levels[field.levelIndex].draw()
        for box in field.getBoxes():
            box.draw()
        turtle.color("#bad4af")# // Todo figure out why the text causes a flicker.
        turtle.goto(field.levels[field.levelIndex].
        mandos[-1].canvasX,field.levels[field.levelIndex].mandos[-1].canvasY)
        turtle.write(field.levels[field.levelIndex].text,
                    font=('Courier', 33, 'italic'), align='left')
    def click(x,y):
        spacex = int((float(x)-field.offset[0])/field.spaceSize +31) # more magic numbers...
        spacey = int((float(y)-field.offset[1])/field.spaceSize +32) # change them I dare you.
        try:
            field.bop(spacex,spacey)
        except IndexError:
            pass
        field.levels[field.levelIndex].checkWin()
    def getBoxes(): # in reality field.spaces should just be a dictionary... // TODO
        return sum([[box for box in boxes if not box is None] for boxes in field.spaces],[])
    def bop(x,y):
        if field.spaces[x][y] is None:
            pass
        else:
            field.hit(x,y)
            field.hit(x+1,y)
            field.hit(x-1,y)
            field.hit(x,y+1)
            field.hit(x,y-1)

"""##############################################################################################"""
def turtleSetup():
    turtle.tracer(0,0)
    turtle.register_shape("sq", # This is a square that denotes the boxes... wait
                        ((field.square.padding,
                            field.square.padding),
                        (field.spaceSize-field.square.padding,
                            field.square.padding),
                        (field.spaceSize-field.square.padding,
                            field.spaceSize-field.square.padding),
                        (field.square.padding,
                            field.spaceSize-field.square.padding)))
    turtle.register_shape("bx", # this is a box that is used to denote the mandos... woops.
                        ((field.level.mando.trim,field.level.mando.trim),
                        (field.level.mando.thickness,
                            field.level.mando.thickness),
                        (field.spaceSize-field.level.mando.thickness,
                            field.level.mando.thickness),
                        (field.spaceSize-field.level.mando.thickness,
                            field.spaceSize-field.level.mando.thickness),
                        (field.level.mando.thickness,
                            field.spaceSize-field.level.mando.thickness),
                        (field.level.mando.thickness,
                            field.level.mando.thickness),
                        (field.level.mando.trim,
                            field.level.mando.trim),
                        (field.level.mando.trim,
                            field.spaceSize-field.level.mando.trim),
                        (field.spaceSize-field.level.mando.trim,
                            field.spaceSize-field.level.mando.trim),
                        (field.spaceSize-field.level.mando.trim,
                            field.level.mando.trim)))
    turtle.ht() # Hide Turtle so you don't get a indicator on the rendering mechanism
    turtle.pu() # Pen up so you don't see the path of the rendering mechanism.
    turtle.onkey(key_space, "space") # Register key events.
    turtle.onkey(key_k, "k")
    turtle.onkey(key_p, "q")
    turtle.getscreen().onclick(field.click) # register the click event
    turtle.listen() # this probably needs to be here for some reason, idk delete it & see for yaself

"""##############################################################################################"""

class main:
    def __init__(self):
        turtleSetup()
        field.setLevel()
        while None is None:
            field.draw()
main() # Look at how clean that main function is! 

https://gist.github.com/Krewn/073a8cf8ed32d0d78171d409d71c74f0?fbclid=IwAR1yb7jREw91wloquHVGEM_bd2whzxPaBJ8GxJR7CcdigLJGlkfmI2RVrg0

I've write very simplified example:

class A:
    class B:
        def __init__(self):
            print(A)
    class C:
        class D:
            def __init__(self):
                print(A)
A.B()
A.C.D()
A().C().D()

And class A is available as in inner as in inner inner class.

By initializing the levels after defining the game field rather than initializing the levels in the game field definition we are able to avoid the unresolved name error. See modified code.

import turtle
import time
import functools
"""######################################### Key press ##########################################"""

def key_space():  # Resets Level
    field.setLevel()

def key_p():      # Skips Level
    field.nextLevel()

def key_k():      # For making levels
    print([(b.x,b.y) for b in field.getBoxes()])
    
"""######################################## Interface(s) ########################################"""

def stamp(self):  # Interface for drawing polygons.
    turtle.goto(self.canvasX+field.offset[0],self.canvasY+field.offset[1])
    turtle.shape(self.shape)
    turtle.color(self.color)
    turtle.stamp()

def frameBoiler(func): #
    @functools.wraps(func)
    def wrapper_frame_boiler(*args,**kwargs):
        turtle.clear()
        #turtle.getscreen().bgpic('Untitled.png')
        r = func(*args, **kwargs)
        turtle.update()
        time.sleep(0.05)
        return r
    return wrapper_frame_boiler

"""######################################### Game Logic #########################################"""

class field:      # Field is an abstract class which facilitates the game Mechanics
    def place(o): # Place translates an objects board position to a pixel or canvas position
        o.canvasX = (o.x-31)*field.spaceSize # 31 beacuse it's half of the magic number...
        o.canvasY = (o.y-31)*field.spaceSize
        return(o) # This method should be a void //TODO refactor...
    class square:
        padding = 2
        color = "#078900"
        shape = "sq" # "sq" is the name of the stamp polygon geometry.
        #//TODO refactor: eliminate redundant literals. see def turtleSetup
        def __init__(self,x,y):
            self.x = x
            self.y = y
            field.place(self)
        def draw(self):
            stamp(self)
    class level:  # Levels are what make the game fun.
        class mando: # These are the spots you have to populate
            thickness = 2 # 
            trim = 1
            color = "#ee0000"
            shape = "bx" # "bx" is the name of the stamp polygon geometry. 
            #//TODO refactor: eliminate redundant literals. see def turtleSetup
            def draw(self): # each mando needs to be visable...
                stamp(self) # line: 15: `def stamp(self)`
            def __init__(self,x,y): # make a mandatory space
                self.x = x
                self.y = y
                field.place(self)
        def __init__(self,text,spots): # make a level
            self.text = text
            self.mandos = []
            self.complete = False
            for spot in spots:
                self.mandos.append(self.mando(spot[0],spot[1]))
        def checkWin(self): # If the boxes on the board match the mandos you beat the level.
            oneLiner = set([(b.x,b.y) for b in field.getBoxes()])
            isTooLong = set([(m.x,m.y) for m in self.mandos])
            self.complete = oneLiner == isTooLong
        def draw(self): # This draws the mandos and the level's acompanying text.
            for mando in self.mandos:
                mando.draw()
            if self.complete:
                field.nextLevel()
    spaces = [[None]*63 for k in range(0,63)] # 63 is the magic number, ralistically it could be 8
    levelIndex = 0   # literally the number indicating what level you're on
    spaceSize = 40   # the number of pixels to a in gmae space.
    offset = [-80,0] # you can arbitrailly move the gmae around the screen...
    def hit(x,y):    # toggle the presence of a box in a space on the board/field.
        try:
            if field.spaces[x][y] is None:
                field.spaces[x][y] = field.place(field.square(x,y))
            else:
                field.spaces[x][y] = None
        except IndexError:
            pass
    def setLevel():  # clears the board puts a box in the middle and places the mandos.
        field.spaces = [[None]*63 for k in range(0,63)]
        field.hit(31,31)
        [field.place(mando) for mando in field.levels[field.levelIndex].mandos]
    def nextLevel(): # the first level is also the level after the last level.
        field.levelIndex += 1
        if field.levelIndex >= len(field.levels):
            field.levelIndex = 0
        field.setLevel()
    @frameBoiler
    def draw():      # this is the draw method for the 
        field.levels[field.levelIndex].draw()
        for box in field.getBoxes():
            box.draw()
        turtle.color("#bad4af")# // Todo figure out why the text causes a flicker.
        turtle.goto(field.levels[field.levelIndex].
        mandos[-1].canvasX,field.levels[field.levelIndex].mandos[-1].canvasY)
        turtle.write(field.levels[field.levelIndex].text,
                    font=('Courier', 33, 'italic'), align='left')
    def click(x,y):
        spacex = int((float(x)-field.offset[0])/field.spaceSize +31) # more magic numbers...
        spacey = int((float(y)-field.offset[1])/field.spaceSize +32) # change them I dare you.
        try:
            field.bop(spacex,spacey)
        except IndexError:
            pass
        field.levels[field.levelIndex].checkWin()
    def getBoxes(): # in reality field.spaces should just be a dictionary... // TODO
        return sum([[box for box in boxes if not box is None] for boxes in field.spaces],[])
    def bop(x,y):
        if field.spaces[x][y] is None:
            pass
        else:
            field.hit(x,y)
            field.hit(x+1,y)
            field.hit(x-1,y)
            field.hit(x,y+1)
            field.hit(x,y-1)


"""################################## Initialize Leveles #########################################"""

field.levels =[field.level("Space For Resets",[(31,33),(31,29),(29,31),(33,31)]),
            field.level("Never\nEat\nShreaded\nWheat",[(27, 31), (28, 30), (28, 32), (29, 30), (29, 32),
            (30, 28), (30, 29), (30, 33), (30, 34), (31, 27), (31, 35), (32, 28), (32, 29),
            (32, 33), (32, 34), (33, 30), (33, 32), (34, 30), (34, 32), (35, 31)]),
            field.level("Ok?",[(27, 31), (31, 27), (31, 35), (35, 31)]),
            field.level("Try:\nPress Space",[(29, 31), (30, 30), (31, 31), (31, 33), (32, 29), (33, 32),
            (34, 31)]),
            field.level("Flex",[(28, 27), (28, 28), (28, 29), (29, 26), (29, 30), (29, 35), (29, 36),
            (30, 29), (30, 31), (30, 33), (30, 34), (30, 35), (30, 37), (31, 28), (31, 29),
            (31, 33), (31, 35), (31, 36), (32, 29), (32, 31), (32, 33), (32, 34), (32, 35),
            (32, 37), (33, 26), (33, 30), (33, 35), (33, 36), (34, 27), (34, 28), (34, 29)]),
            field.level("Blast Off",[(28, 28), (28, 29), (28, 31), (29, 27), (29, 31), (29, 32), (30, 28),
            (30, 29), (30, 31), (30, 32), (30, 33), (30, 34), (31, 33), (31, 35), (32, 28), 
            (32, 29), (32, 31), (32, 32), (32, 33), (32, 34), (33, 27), (33, 31), (33, 32),
            (34, 28), (34, 29), (34, 31)]),
            field.level("Space\nInvaders",[(27, 31), (28, 30), (28, 32), (29, 27), (29, 28), (29, 30),
            (29, 31), (30, 26), (30, 28), (30, 29), (30, 33), (31, 27), (31, 28), (31, 31),
            (31, 32), (32, 26), (32, 28), (32, 29), (32, 33), (33, 27), (33, 28), (33, 30),
            (33, 31), (34, 30), (34, 32), (35, 31)]),
            field.level("big oof",[(31,31),(32,31),(31,33)])]

"""##############################################################################################"""
def turtleSetup():
    turtle.tracer(0,0)
    turtle.register_shape("sq", # This is a square that denotes the boxes... wait
                        ((field.square.padding,
                            field.square.padding),
                        (field.spaceSize-field.square.padding,
                            field.square.padding),
                        (field.spaceSize-field.square.padding,
                            field.spaceSize-field.square.padding),
                        (field.square.padding,
                            field.spaceSize-field.square.padding)))
    turtle.register_shape("bx", # this is a box that is used to denote the mandos... woops.
                        ((field.level.mando.trim,field.level.mando.trim),
                        (field.level.mando.thickness,
                            field.level.mando.thickness),
                        (field.spaceSize-field.level.mando.thickness,
                            field.level.mando.thickness),
                        (field.spaceSize-field.level.mando.thickness,
                            field.spaceSize-field.level.mando.thickness),
                        (field.level.mando.thickness,
                            field.spaceSize-field.level.mando.thickness),
                        (field.level.mando.thickness,
                            field.level.mando.thickness),
                        (field.level.mando.trim,
                            field.level.mando.trim),
                        (field.level.mando.trim,
                            field.spaceSize-field.level.mando.trim),
                        (field.spaceSize-field.level.mando.trim,
                            field.spaceSize-field.level.mando.trim),
                        (field.spaceSize-field.level.mando.trim,
                            field.level.mando.trim)))
    turtle.ht() # Hide Turtle so you don't get a indicator on the rendering mechanism
    turtle.pu() # Pen up so you don't see the path of the rendering mechanism.
    turtle.onkey(key_space, "space") # Register key events.
    turtle.onkey(key_k, "k")
    turtle.onkey(key_p, "q")
    turtle.getscreen().onclick(field.click) # register the click event
    turtle.listen() # this probably needs to be here for some reason, idk delete it & see for yaself

"""##############################################################################################"""

class main:
    def __init__(self):
        turtleSetup()
        field.setLevel()
        while None is None:
            field.draw()
main() # Look at how clean that main function is! ```

  

in inner class and inner inner class only difference is inner class/parent class can have several inner class but inner class mostly avoid it.In python inner class is know as nested class.

This doesn't have to do with whether the class is outer or inner. It only has to do with naming scope.

def assigns a function to a name

Take the following statement:

myfunction = lambda x: x

The above statement defines a name myfunction that points to a function. Compare with this one:

def myfunction(x):
    return x

Although this form has some minor differences (eg myfunction.__name__ will be myfunction after the second form, but <lambda> after the first form), the second statement is exactly equivalent to the first statement in that it defines a name myfunction that happens to be pointing to a function.

class assigns a class to a name

Class definitions work the same (although they have no lambda equivalent):

class Outer:
    def myfunc(self):
        Outer

    class Inner:
        def myfunc(self):
            Outer

Outer.Inner().myfunc()  # No error

What this does is that it defines a (global) name Outer that happens to be pointing to a class. When the last line of the above snippet runs, the Outer name has been defined. When Outer.Inner.myfunc() is executing, it doesn't do anything other than access the name Outer .

In fact, you could just as well write this:

class A:
    def myfunc(self):
        B

class B:
    pass

A().myfunc()

When the last line of this snippet executes, (global) names A and B have been defined; when A.myfunc() runs, these names exist.

While a function is being created, the name pointing to it doesn't exist

myfunction = lambda x: x

This assignment assigns the expression lambda x: x to the name myfunction . The expression is evaluated first, and the function is constructed, and only when the result of the expression—the function—is ready does the interpreter create the myfunction name and make it point to the newly created function.

Likewise,

def myfunction(x):
    return x

The same thing happens. A function is created, and when the function is ready, the name myfunction is created and made to point to the newly created function.

While a class is being created, the name pointing to it doesn't exist

class Outer:
    class Inner:
        my_class_attribute = Outer  # This will cause an error

While the last line of this snippet is being executed, the class Outer is still being created. Since its creation hasn't finished, the name Outer hasn't been created yet. Only after the class Outer had been created would the interpreter create a name Outer and make it point to the newly created class. Since no such name exists yet while the last line of the snippet is being executed, it will throw the error that the name Outer is undefined.

It would throw the same error if you did this:

class A:
    my_class_attribute = A

But note that you can do this:

class A:
    pass


class A:
    this_points_to_the_previous_A = A

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM