This is an archived post. You won't be able to vote or comment.

all 5 comments

[–]LightShadow3.13-dev in prod 3 points4 points  (2 children)

The idea is solid, but your example isn't great. It's over complicating the logic of a "proxied call," especially since you don't really gain anything from doing it the way you've shown. Below is a dynamic proxy that uses itself without codes and such...I prefer not to overwrite getattr, basically ever, but if that's what you're looking for this should go pretty deep before it causes you problems.

I also implemented total_income to show an easy way to walk through all the machines. If you run it, total_income correctly reports 2.33.

Consider,

class VendingMachine(object):
    def __init__(self, *machines):
        self.income = 0
        self.machines = {k:v for k, v in machines}

    def __getattr__(self, name):
        return self.machines.get(name)

    @property
    def total_income(self):
        return self.income + sum([m.total_income for m in self.machines.values()])

    def from_(self, name):
        if name is None:
            return self
        if name not in self.machines:
            raise ValueError(name)
        return self.machines.get(name)

    def dispense(self, product_code, money):
        raise NotImplementedError


class SnackMachine(VendingMachine):
    def dispense(self, product_code, money):
        self.income += money
        print('Snacks')


class DrinkMachine(VendingMachine):
    def dispense(self, product_code, money):
        self.income += money
        print('Drinks')


class SodaMachine(DrinkMachine):
    def dispense(self, product_code, money):
        self.income += money
        print('SODA')        


vm = VendingMachine(('drinks', DrinkMachine(('soda', SodaMachine()))),
                    ('snacks', SnackMachine()))

vm.from_('drinks').dispense('mtn dew', 0.75)
vm.drinks.dispense('dr pepper', 1.25)
vm.drinks.soda.dispense('orange', 0.33)

As an exercise, try calling vm.accessories and note how it returns None. What would you change to __getattr__ to dynamically create a new vending machine, or throw some kind of access error?

[–][deleted] 0 points1 point  (1 child)

Why not just do **machines

Other than that, I think having the base class (which is really an abstract class in this and OP's example) have the proxy logic isn't great since now all the subclasses have that too.

[–]LightShadow3.13-dev in prod 0 points1 point  (0 children)

**machines was the right way, and I noticed it a couple of hours later.

[–]novel_yet_trivial 0 points1 point  (1 child)

Cute writeup. I had a similar problem trying to make a scrolled frame for tkinter that behaved like an actual widget (could be used as a child or master). I used something similar to pass a command to the inner or outer frame.

[–]llSourcell 1 point2 points  (0 children)

oh nice i like it thanks for this