3

我刚开始接触 Python,这是一个关于类的逻辑和实现的非常普遍的问题。我为这个问题的基本层面道歉,但希望它对其他人也有用。这里有一些上下文可以更清楚地说明:

语境

  • 我想创建一个代表图像的类。此图像包括 3 个波段(R、G、B,与 3 个不同的文件相关联)和一些元数据(一个文件,其中包含 3 个波段文件的文件路径和其他信息,如相机、地理位置等)。

  • 对于我思考问题的方式,Image 类应该包含一个 Metadata 类型的属性和三个 Band 类型的属性。

  • 类元数据应该具有读取和返回各种信息的方法

  • Class Band 应该有对每个栅格波段进行分析和处理的方法。但是,这些方法可能需要访问 Metadata 中包含的信息

我的代码

所以这就是我要做的:

class Metadata:
    def __init__(self, meta_file_path):
        self.Path = meta_file_path
    def ReadBandPath(self,band_number):
        [...]
    def ReadLocation(self):
        [...]
    def ReadCameraInfo(self):
        [...]
    def GetSomeOtherInfo(self):
        [...]

class Band:
    def __init__(self,Metadata, band_number):
        self.Meta = Metadata
        self.Number = band_number
        self.Path = self.Meta.ReadBandPath(self.Number)
    def DoSomething(self):
        self.Meta.GetSomeOtherInfo()
        [...]

class Image:  
    def __init__(self, meta_file_path)
        self.Meta = Metadata(meta_file_path)
        self.Band1 = Band(self.Meta, 1)
        self.Band2 = Band(self.Meta, 2)
        self.Band3 = Band(self.Meta, 3)
    def SaveAsPng(dest_file):
        [...]  

我的问题

我的方式对我来说似乎有点多余,更重要的是它似乎是“静态的”。看起来如果我在创建 Image.BandN 之后更新 Image.Meta 中的一些信息,那么 Image.BandN.Meta 不会同时更新,对吧?

  1. 我是否正确设置和实施问题?
  2. 以动态方式将元数据属性传递给 Band 对象的最聪明的方法是什么?
4

5 回答 5

2

OP问:1:我是否正确设置和实施问题?
我想
通过使用继承为您的类实现提供一个替代方案(较少冗余)。
下面的代码是用 Python 2.7.3 编写的

class Metadata(object):
    def __init__(self, meta_file_path):
        self.Path= meta_file_path
    def ReadBandPath(self,band_number):
        print 'ReadBandPath: ', str(band_number)
    def ReadLocation(self):
        print 'ReadLocation'
    def ReadCameraInfo(self):
        print 'ReadCameraInfo'
    def GetSomeOtherInfo(self):
        print 'GetSomeOtherInfo'

class Band(Metadata):
    def __init__(self, file_path, band_number):
        Metadata.__init__(self, file_path )
        self.number= band_number
        self.Path= self.ReadBandPath(self.number)
    def DoSomething(self):
        self.GetSomeOtherInfo()

class Image(Band):  
    def __init__(self, file_path, band_number, destfile):
        Band.__init__(self, file_path, band_number)
        self.pngfile= destfile
    def SaveAsPng(self):
        print 'Saved as png : ', self.pngfile  

# Now you can create instances of Image like this:
Band1= Image('samplepath1',1,'file4.png')
Band2= Image('samplepath2',2,'filex.png')
Band3= Image('samplepath3',3,'afile.png')
# Methods and attributes from Metadata , Band and Image  : 
Band3.SaveAsPng()
Band2.DoSomething()
Band1.ReadCameraInfo()
print 'Band1: ',Band1.number
print 'Band2: ',Band2.number
print 'Band3: ',Band3.number  
# etc...
于 2013-02-16T01:35:09.390 回答
1

看起来如果我在创建 Image.BandN 之后更新 Image.Meta 中的一些信息,那么 Image.BandN.Meta 不会同时更新,对吧?

这取决于您如何进行更新。创建您的Image实例(我将调用它img)后,img.Meta它们img.BandN.Meta是同一个对象。

如果您为其分配新值,img.Metaimg.BandN.Meta不会更新,因为img.Meta现在是一个新对象并且img.BandN.Meta仍然是原始对象。

但是,如果您修改img.Meta,例如img.Meta.some_attribute = new_value, thenimg.BandN.Meta也将被更新,因为它们仍然是同一个对象。

只要您正在修改img.Meta而不是给它一个新值,您的代码看起来就很好。

于 2013-02-15T23:13:32.223 回答
1

看起来如果我在创建 Image.BandN 之后更新 Image.Meta 中的一些信息,那么 Image.BandN.Meta 不会同时更新,对吧?

不,这不是问题;my_image.Band1.Meta将是对与 相同的对象的引用my_image.Meta

只有当您重新分配my_mage.Meta以命名不同的对象(而不是改变它命名的对象)时,您才会遇到问题。

但是您可以自己测试,通过打印id(my_image.Meta)andid(my_image.Band1.Meta)或检查my_image.Meta is my_image.Band1.Meta.

我的方式对我来说似乎有点多余,更重要的是它似乎是“静态的”。

嗯,它有点多余和静态,因为它只处理三个波段,如果你想为 CMYK 使用相同的代码,则需要在所有地方进行更改。如果这是您可能想要做的事情,您可能需要考虑:

self.Bands = []
self.Bands.append(Band(self.Meta, 1))
self.Bands.append(Band(self.Meta, 2))
self.Bands.append(Band(self.Meta, 3))

或者:

self.Bands = [Band(self.Meta, i) for i in range(3)]

或者,如果 RGB 是固有且不可更改的部分,您可能希望使用名称而不是数字(只是'R'', 'G', 'B')。然后,您可能希望将它们放入集合而不是单独的变量中:

self.Bands = {name: Band(self.Meta, name) for name in ('R', 'G', 'B')}
于 2013-02-15T23:17:36.727 回答
1

这一切似乎都是合理的。

的任何方法Band,如果需要查阅元数据,都可以通过self.Meta引用进行。

顺便说一句,考虑采用 Python Style Guide 中的命名约定,即,只为类名保留 CapitalizedWords;对参数、属性、方法和变量使用 lower_case_with_underscores。(Metadata参数 toBand.__init__正在隐藏Metadata类。)

于 2013-02-15T23:18:22.840 回答
1

您已经将图像分解为三个类,但是 Band 和 MetaData 是紧密耦合的,而 Image 并没有做太多的事情。可能 Band 更简单地表示为整数或浮点数的数组。

我不会尝试设计对象和类层次结构,而是从最简单的实现开始。如果类变得太大或代码变得笨拙,那么您可以开始分离类。您会发现,一旦您到了需要清理它的地步,简单、扁平的代码比精心设计的对象层次结构更容易重塑。

class Image(object):
    def __init__(self, meta_file_path):
        self.meta_file_path = meta_file_path
        self.bands = {}
        for b in 'RGB':
            self.bands[b] = self.load_band(b)

    def read_location(self):
        ...

    def process_band(self, b):
        ...
于 2013-02-16T09:44:25.687 回答