简体   繁体   中英

multiple inheritance, validation, python

I have a pair classes (Substance and Regime) that I'd like to hold lookup values for a common child class (Population), as shown. I want to make sure that instances of Population can only be created based on existing instances of the two parent classes Substance and Regime. My code (obviously) does not work the way I want, and I've been fiddling with this for a while, coming up with various BAD ideas: including global variables, breaking stuff into modules, spaghetti marinara everywhere! This seems like it should be easy to do in an explicit and direct way. Any ideas, please? (And no, I don't think multiple inheritance is correct here, but this was my latest try...)

class Substance:
    def __init__(self, name):
        self.name = name

class Regime: 
    def __init__(self, name):
        self.name = name

class Population(Substance, Regime):
    def __init__(self, subs, reg, qty):
        self.subs = subs  
        self.reg  = reg   # this should validate against regime_dict
        self.qty = qty


substance_dict = {'H' : substance('hydrogen'), 
                  'C': substance('carbon')}
regime_dict    = {'S1': regime('Surface 1'), 
                  'S2': regime(Surface 2)}
pop = population('H', 'S1', 1000) # the first two args should validate against
                                  # substance_dict and regime_dict

Why not just do this:

pop = population(substance_dict['H'], regime_dict['S1'], 1000)

That is, instead of passing in the labels of the substance and regime, pass in the actual substance and regime objects. You may have to adjust your code in Population a bit to deal with this, because now it should expect a Subtance and a Regime, not two strings, but I think it will be much simpler conceptually.

If you need to enforce that Population receives one Regime and one Substance , I'd try something like this:

class Substance(object):
    def __init__(self, name):
        self.name = name

class Regime(object): 
    def __init__(self, name):
        self.name = name

class Population(object):
    def __init__(self, subs, reg, qty):
    if not isinstance(subs, Substance):
      raise TypeError("subs must be a Substance")
    if not isinstance(reg, Regime):
      raise TypeError("reg must be a Regime")
    self.subs = subs  
    self.reg  = reg   # this should validate against regime_dict
    self.qty = qty


substance_dict = {'H' : Substance('hydrogen'), 
                  'C': Substance('carbon')}
regime_dict    = {'S1': Regime('Surface 1'), 
                  'S2': Regime('Surface 2')}
pop = Population(substance_dict['H'], regime_dict['S1'], 1000)
pop_break = Population(substance_dict['H'], substance_dict['C'], 1000)

If you also want to verify that the arguments are in the dictionaries, then use something like this:

class Population(object):
    def __init__(self, subs, reg, qty):
    if not isinstance(subs, Substance):
      raise TypeError("subs must be a Substance")
    if not subs in substance_dict.values():
      raise ValueError("subs %s is not in the substance_dict" % subs.name)
    if not isinstance(reg, Regime):
      raise TypeError("reg must be a Regime")
    if not reg in regime_dict.values():
      raise ValueError("reg %s is not in the regime_dict" % reg.name)
    self.subs = subs  
    self.reg  = reg   # this should validate against regime_dict
    self.qty = qty


substance_dict = {'H' : Substance('hydrogen'), 
                  'C': Substance('carbon')}
regime_dict    = {'S1': Regime('Surface 1'), 
                  'S2': Regime('Surface 2')}
pop = Population(substance_dict['H'], regime_dict['S1'], 1000)
pop_break = Population(Substance('radon'), substance_dict['C'], 1000)

When you try to create pop_break, you'll receive an exception:

Traceback (most recent call last):
  File "./stack7_1.py", line 29, in <module>
    pop_break = Population(Substance('radon'), substance_dict['C'], 1000)
  File "./stack7_1.py", line 14, in __init__
    raise ValueError("subs %s is not in the substance_dict" % subs.name)
ValueError: subs radon is not in the substance_dict

Careful, because this will also break:

substance_dict = {'H' : Substance('hydrogen'), 
                  'C': Substance('carbon')}
regime_dict    = {'S1': Regime('Surface 1'), 
                  'S2': Regime('Surface 2')}
pop = Population(substance_dict['H'], regime_dict['S1'], 1000)
pop_break = Population(Substance('hydrogen'), substance_dict['C'], 1000)

Since the Substance('hydrogen') of the dictionary is a different instance than the Substance('hydrogen') of the Population(Substance('hydrogen'), ... )

Try this:

class Population(Substance, Regime):
    def __init__(self, subs, reg, qty):
        #Etc...

ok, here is the right way to do what I wanted, thanks for your comments!

class Substance:
    def __init__(self, symbol, name):
        self.symbol = symbol
        self.name = name

class Regime: 
    def __init__(self, symbol, name):
        self.symbol = symbol
        self.name = name

class Population(Substance, Regime):
    def __init__(self, subs, reg, qty):
        self.subs = subs  
        self.reg  = reg   # this should validate against regime_dict
        self.qty = qty
        self.key = subs.symbol+reg.symbol

s1  = Substance('H', 'hydrogen')
s2  = Substance('C', 'carbon')
reg = Regime('S1', 'Surface 1')

pop = Population(s1, reg, 1000)

print pop.subs.symbol
print pop.reg.symbol
print pop.key

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM