问题
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?
- Am I setting and implementing the problem correctly?
- 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