Can an attribute access another attribute?

↘锁芯ラ 提交于 2021-02-09 11:13:50

问题


I am at the very beginning with Python, and this is a very general question on the logic and implementation of classes. I apologize for the basic level of the question, but hopefully it will be useful for others too. Here is some context to make it clearer:

Context

  • I want to create a class representing an image. This image includes 3 bands (R,G,B, associated with 3 different files) and some metadata (one file, which contains the file path of the 3 bands files and other info like camera, geo location, etc.).

  • For my way of thinking the problem, the class Image should include an attribute of type Metadata and three attributes of type Band.

  • Class Metadata should have methods to read and return various info

  • Class Band should have methods for analysis and processing of each raster band. However, these methods may need to access information enclosed in Metadata.

My code

So here is what I would do:

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):
        [...]  

My problem

My way seems a little redundant to me, and more importantly it seems "static". It looks like if I update some info in Image.Meta after creating Image.BandN, then Image.BandN.Meta won't be simultaneously updated, right?

  1. Am I setting and implementing the problem correctly?
  2. What would be the smartest way of passing a Metadata attribute to the a Band object in a dynamic way?

回答1:


It looks like if I update some info in Image.Meta after creating Image.BandN, then Image.BandN.Meta won't be simultaneously updated, right?

No, this isn't a problem; my_image.Band1.Meta will be a reference to the same object as my_image.Meta.

It's only if you reassign my_mage.Meta to name a different object (as opposed to mutating the object it names) that you'll have a problem.

But you can test this yourself, by printing out id(my_image.Meta) and id(my_image.Band1.Meta), or checking my_image.Meta is my_image.Band1.Meta.

My way seems a little redundant to me, and more importantly it seems "static".

Well, it is a bit redundant and static in that it handles exactly three bands, and would need changes all over the place if you wanted to use the same code for, say, CMYK. If that's something you might ever want to do, you might want to consider:

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

Or:

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

Alternatively, if RGB is an inherent and unchangeable part, you may want to use names instead of numbers (just 'R'', 'G', 'B'). And then, you might still want to put them into a collection instead of separate variables:

self.Bands = {name: Band(self.Meta, name) for name in ('R', 'G', 'B')}



回答2:


OP asked: 1: Am I setting and implementing the problem correctly?
I would like to offer an alternative ( less redundant)
for your class implementation by using inheritance.
The code below is written in 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...



回答3:


It looks like if I update some info in Image.Meta after creating Image.BandN, then Image.BandN.Meta won't be simultaneously updated, right?

This depends on how you do the updating. After creating your Image instance (which I will call img), img.Meta and img.BandN.Meta are the same object.

If you assign a new value to img.Meta then img.BandN.Meta will not be updated, since img.Meta is now a new object and img.BandN.Meta is still the original.

However if you modify img.Meta, for example img.Meta.some_attribute = new_value, then img.BandN.Meta will also be updated since they are still the same object.

Your code looks fine the way it is as long as you are modifying img.Meta instead of giving it a new value.




回答4:


That all seems reasonable.

Any method of Band, if it needs to consult the metadata, can do so via the self.Meta reference.

As an aside, consider adopting the naming convention from the Python Style Guide, i.e., reserve CapitalizedWords for class names only; use lower_case_with_underscores for parameters, attributes, methods, and variables. (The Metadata parameter to Band.__init__ is shadowing the Metadata class.)




回答5:


You've dissected the image into three classes, but Band and MetaData are tightly coupled, and Image doesn't do much. Possibly Band is more simply represented as an array of ints or floats.

Rather than try to design an object and class hierarchy, I'd start with the simplest implementation possible. If the class gets too big or the code becomes unwieldy, then you can start separating classes out. You'll find that simple, flat code is easier to reshape than highly crafted object hierarchies once you get to the point of needing to clean it up.

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


来源:https://stackoverflow.com/questions/14904878/can-an-attribute-access-another-attribute

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!