我对Python中的类有几个问题。
有一个Bullet类,它代表了一个计算机游戏中的抽象弹丸,并且会有很多它的实例(你看,我正试图制作一个“子弹地狱”游戏:)
实例具有相同的图像和移动功能,同时具有不同的位置。
在下面的代码中,我试图将常见的东西放到类属性中,并使用class_init
类方法加载它们的值。目标是对数据进行重复数据删除并节省一些内存。
Q1 :class_init
方法是否正确?也许有一种更优雅的方式。
Q2 :它是否会真正提高内存使用率?
已解决:感谢您的提示,大家好!我想我的代码并没有完全错误,但可以改进。要读一下Python中的metaclases,slot和profiling。还有很长的路要走,感叹。无论如何,谢谢!
class Bullet(object):
@classmethod
def class_init(cls, image, movement_function):
cls.image = image
cls.movement_function = movement_function
def __init__(self, pos):
super(Bullet, self).__init__()
self.pos = pos
if __name__ == '__main__':
Bullet.class_init('sprite.png', lambda x: x + 1)
b1 = Bullet((10,10))
b2 = Bullet((20,20))
print 'pos', id(b1.pos) == id(b2.pos)
print 'image', id(b1.image) == id(b2.image)
print 'movement_function', id(b1.movement_function) == id(b2.movement_function)
答案 0 :(得分:1)
Q1:那个class_init方法是正确的吗?也许有一种更优雅的方式。
没错。也许更惯用的方法是作为元类的__init__
,但两种方式都可以。
Q2:它是否会真正提高内存使用率?
可能不是很多,因为没有复制图像和功能 - 只复制对这些对象的引用。
了解某些内容是否可以提高速度或内存使用情况的方法是对其进行分析。在你发现问题之前,不要做可以优化某些事情的技巧。
cls.movement_function = movement_function
为什么不是movement_function
只是一种方法?
答案 1 :(得分:1)
Q1:那个class_init方法是正确的吗?也许有更优雅的方式。
这是否是正确的事情很难说清楚。但这绝对不是一个糟糕的方法。您正在使用类方法来定义类属性,它有意义并且看起来很好。如果您认为是OOP,那么您将类数据封装在类方法中。
尽管如此,movement_function
是一个处理实例成员的行为。因此,最好将其定义为实例方法,并在需要具有不同movement
s的情况下使用类继承进行更改。
Q2:它是否会真正提高内存使用率?
对于图像,它实际上取决于您如何初始化它。想象一下:
class Bullet2:
def __init__(self, image):
self.image = image
如果你这样做:
for _ in range(1,10000):
with open('/path/to/image.png', 'r') as f:
Bullet2(YourImage(f.read()))
然后它将需要10000个图像实例。这会消耗很多记忆。
虽然你这样做:
with open('/path/to/image.png', 'r') as f:
img = YourImage(f.read())
for _ in range(1,10000):
Bullet2(img)
然后你将有10000个引用同一个实例。哪个会占用与你相同的内存量:
with open('/path/to/image.png', 'r') as f:
img = YourImage(f.read())
Bullet.class_init(img)
for _ in range(1,10000):
Bullet()
如果您的解决方案具有某种优点,那么您是否想要使用单一方法更改所有实例的图像。虽然我最好使用一个名为setup_image()
的方法,而不是太通用的class_init()
,它不会暗示它的作用。
如果您想一次更改每个实例的图片,那么您最好在Bullet
中使用引用,就像我在使用Bullet2
一样:
class Bullet2:
def __init__(self, image):
self.image = image
def change_image(self, img):
self.image = img
关于将函数的回调设置为类成员,或者为类创建方法,它将等效于内存,但第二个选项会更好,因为它会更具可读性,它将定义你班级的行为。如果您想要更改方法的内容,则可以使用类继承。
答案 2 :(得分:1)
定制类的更惯用的方法是定义一个工厂函数,它将返回Bullet
的子类。如果您不关心生成的子类的名称,则可以跳过cls_name
参数并让make_bullet
使用硬编码名称。
class Bullet(object):
def __init__(self, pos):
super(Bullet, self).__init__()
self.pos = pos
def make_bullet(cls_name, image, movement_function):
return type(cls_name, (Bullet,), {'image': image,
'movement_function': movement_function })
if __name__ == '__main__':
MyBullet = make_bullet('MyBullet', 'sprite.png', lambda x: x+1)
b1 = MyBullet((10,10))
b2 = MyBullet((20,20))
print 'pos', id(b1.pos) == id(b2.pos)
print 'image', id(b1.image) == id(b2.image)
print 'movement_function', id(b1.movement_function) == id(b2.movement_function)