【问题标题】:Can an attribute access another attribute?一个属性可以访问另一个属性吗?
【发布时间】:2013-02-15 23:07:40
【问题描述】:

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

上下文

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

  • 按照我的思路,Image类应该包含一个Metadata类型的属性和三个Band类型的属性。

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

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

我的代码

这就是我要做的:

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 对象的最聪明的方法是什么?

【问题讨论】:

  • 首先,“类属性”通常意味着上的属性,而不是类的每个实例上的属性。只是“属性”通常就足够了;当您必须避免歧义时,请尝试“实例属性”或“类实例属性”。
  • 第二,用与类相同的样式命名属性会让人读起来很困惑,谈论变化更让人困惑。如果你关注PEP 8,我可以说image.meta,你会假设我在谈论一个实例image,或者类Image。但如果你不这样做,我必须特意说“image.meta,其中imageImage 实例。
  • 我明白了,谢谢@abarnert 的提示!

标签: python class attributes


【解决方案1】:

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...

【讨论】:

    【解决方案2】:

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

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

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

    但您可以自己测试,通过打印出id(my_image.Meta)id(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')}
    

    【讨论】:

      【解决方案3】:

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

      这取决于您如何进行更新。创建Image 实例(我将其称为img)后,img.Metaimg.BandN.Meta 是同一个对象。

      如果您为img.Meta 分配一个新值,那么img.BandN.Meta 将不会被更新,因为img.Meta 现在是一个新对象,而img.BandN.Meta 仍然是原始对象。

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

      只要您修改 img.Meta 而不是为其赋予新值,您的代码看起来就很好。

      【讨论】:

      • 谢谢!我会投票赞成你的答案,但我没有足够的“特权”:)
      【解决方案4】:

      这一切似乎都是合理的。

      Band的任何方法,如果需要查阅元数据,可以通过self.Meta参考。

      顺便说一句,考虑采用 Python 样式指南中的命名约定,即,只为类名保留 CapitalizedWords;对参数、属性、方法和变量使用 lower_case_with_underscores。 (Band.__init__Metadata 参数隐藏了 Metadata 类。)

      【讨论】:

        【解决方案5】:

        您已将图像分解为三个类,但 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):
                ...
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-10-03
          • 2013-05-06
          • 2015-09-10
          • 2022-08-23
          • 2017-10-17
          • 1970-01-01
          • 2021-11-27
          • 1970-01-01
          相关资源
          最近更新 更多