python 中的内部 class 和内部内部 class 有什么区别?
What's the difference between an inner class and an inner inner class in python?
field
不应该在第 50 行未定义吗?据我了解,内部嵌套 类 对外部 类 不可见,因为我 运行 进入第 65 行......看起来有点不一致,我希望有一个更好地理解外部嵌套 类 可见性以避免将来出现任何混淆。
"""######################################### 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!
我写了一个非常简单的例子:
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()
并且class A
可用在inner as in inner inner class.
通过在定义游戏字段后初始化关卡而不是在游戏字段定义中初始化关卡,我们能够避免未解析的名称错误。查看修改后的代码。
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和inner inner class唯一的区别是inner class/parent class可以有多个inner class但是inner class主要避免 it.In python 内部 class 被称为嵌套 class.
这与class是外层还是内层无关。它只与命名范围有关。
def
将一个函数赋给一个名字
取下面的语句:
myfunction = lambda x: x
上面的语句定义了一个指向函数的名字myfunction
。与这个比较:
def myfunction(x):
return x
虽然这种形式有一些细微差别(例如 myfunction.__name__
在第二种形式之后是 myfunction
,但是在第一种形式之后是 <lambda>
),第二个语句完全等同于第一条语句,它定义了一个恰好指向函数的名称 myfunction
。
class
将 class 分配给名称
Class 定义工作相同(尽管它们没有等效的 lambda):
class Outer:
def myfunc(self):
Outer
class Inner:
def myfunc(self):
Outer
Outer.Inner().myfunc() # No error
它的作用是定义一个(全局)名称 Outer
,恰好指向 class。当上述代码片段的最后一行运行时,Outer
名称已定义。当 Outer.Inner.myfunc()
正在执行时,它除了访问名称 Outer
.
之外什么都不做
事实上,你也可以这样写:
class A:
def myfunc(self):
B
class B:
pass
A().myfunc()
执行此代码段的最后一行时,(全局)名称 A
和 B
已定义;当 A.myfunc()
运行时,这些名称存在。
创建函数时,指向它的名称不存在
myfunction = lambda x: x
此赋值将表达式 lambda x: x
赋给名称 myfunction
。首先计算表达式,然后构造函数,只有当表达式的结果(函数)准备就绪时,解释器才会创建 myfunction
名称并使其指向新创建的函数。
同样,
def myfunction(x):
return x
同样的事情发生了。创建一个函数,当函数准备就绪时,创建名称 myfunction
并指向新创建的函数。
创建class时,指向它的名称不存在
class Outer:
class Inner:
my_class_attribute = Outer # This will cause an error
虽然正在执行此代码段的最后一行,但仍在创建 class Outer
。由于其创建尚未完成,因此 name Outer
尚未创建。只有在创建 class Outer
之后,解释器才会创建一个 name Outer
并使其成为指向新创建的class。由于在执行代码段的最后一行时尚不存在这样的名称,因此将抛出名称 Outer
未定义的错误。
如果你这样做,它会抛出同样的错误:
class A:
my_class_attribute = A
但请注意,您可以这样做:
class A:
pass
class A:
this_points_to_the_previous_A = A
field
不应该在第 50 行未定义吗?据我了解,内部嵌套 类 对外部 类 不可见,因为我 运行 进入第 65 行......看起来有点不一致,我希望有一个更好地理解外部嵌套 类 可见性以避免将来出现任何混淆。
"""######################################### 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!
我写了一个非常简单的例子:
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()
并且class A
可用在inner as in inner inner class.
通过在定义游戏字段后初始化关卡而不是在游戏字段定义中初始化关卡,我们能够避免未解析的名称错误。查看修改后的代码。
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和inner inner class唯一的区别是inner class/parent class可以有多个inner class但是inner class主要避免 it.In python 内部 class 被称为嵌套 class.
这与class是外层还是内层无关。它只与命名范围有关。
def
将一个函数赋给一个名字
取下面的语句:
myfunction = lambda x: x
上面的语句定义了一个指向函数的名字myfunction
。与这个比较:
def myfunction(x):
return x
虽然这种形式有一些细微差别(例如 myfunction.__name__
在第二种形式之后是 myfunction
,但是在第一种形式之后是 <lambda>
),第二个语句完全等同于第一条语句,它定义了一个恰好指向函数的名称 myfunction
。
class
将 class 分配给名称
Class 定义工作相同(尽管它们没有等效的 lambda):
class Outer:
def myfunc(self):
Outer
class Inner:
def myfunc(self):
Outer
Outer.Inner().myfunc() # No error
它的作用是定义一个(全局)名称 Outer
,恰好指向 class。当上述代码片段的最后一行运行时,Outer
名称已定义。当 Outer.Inner.myfunc()
正在执行时,它除了访问名称 Outer
.
事实上,你也可以这样写:
class A:
def myfunc(self):
B
class B:
pass
A().myfunc()
执行此代码段的最后一行时,(全局)名称 A
和 B
已定义;当 A.myfunc()
运行时,这些名称存在。
创建函数时,指向它的名称不存在
myfunction = lambda x: x
此赋值将表达式 lambda x: x
赋给名称 myfunction
。首先计算表达式,然后构造函数,只有当表达式的结果(函数)准备就绪时,解释器才会创建 myfunction
名称并使其指向新创建的函数。
同样,
def myfunction(x):
return x
同样的事情发生了。创建一个函数,当函数准备就绪时,创建名称 myfunction
并指向新创建的函数。
创建class时,指向它的名称不存在
class Outer:
class Inner:
my_class_attribute = Outer # This will cause an error
虽然正在执行此代码段的最后一行,但仍在创建 class Outer
。由于其创建尚未完成,因此 name Outer
尚未创建。只有在创建 class Outer
之后,解释器才会创建一个 name Outer
并使其成为指向新创建的class。由于在执行代码段的最后一行时尚不存在这样的名称,因此将抛出名称 Outer
未定义的错误。
如果你这样做,它会抛出同样的错误:
class A:
my_class_attribute = A
但请注意,您可以这样做:
class A:
pass
class A:
this_points_to_the_previous_A = A