make it so that when you change one attribute you change another

  • (2 Pages)
  • +
  • 1
  • 2

18 Replies - 826 Views - Last Post: 05 January 2019 - 09:46 PM Rate Topic: -----

#1 bobsmith76   User is offline

  • D.I.C Regular

Reputation: 11
  • View blog
  • Posts: 343
  • Joined: 14-February 17

make it so that when you change one attribute you change another

Posted 30 December 2018 - 08:24 PM

I'm trying to build a class composed of two subclasses. When you change the attribute of the parent class, you automatically change the attributes of two child classes. First, the example is simplified, the real world situation is more complicated. I realize some people might want me to use inheritance but I prefer nested classes to inheritance. I don't like cluttering up my namespace with attributes that I don't need and nested classes allow me to do that, so just indulge me here, ok? I know from the example below it seems like I'm creating a bunch of useless work but trust me I need two classes nested within a class because the two classes are used for different operations. So what I'm trying to do here is set it up so that when you change the `subj` you automatically change those attributes in the two nested classes. Also I used `setattr` because eventually I'm going to be looping through a list of attributes. Let me know if you think this is the best way of doing things.


class sent:
    def __init__(self):
        self.subj = "b"
        self.sent2().main(self)
        self.sent3().main(self)

    @property
    def subj(self):
        return self._subj

    @subj.setter
    def subj(self, value):
        self._subj = value
        self.sent2.subj = value
        self.sent3.subj = value

    class sent2:
        def main(self, cls):
            self.subj = cls.subj

    class sent3:
        def main(self, cls):
            self.subj = cls.subj

c = sent()
setattr(c, "subj", "e")



Is This A Good Question/Topic? 0
  • +

Replies To: make it so that when you change one attribute you change another

#2 jon.kiparsky   User is offline

  • Beginner
  • member icon


Reputation: 11374
  • View blog
  • Posts: 19,406
  • Joined: 19-March 11

Re: make it so that when you change one attribute you change another

Posted 30 December 2018 - 10:51 PM

Okay, so let's start with a terminology note. These are not child classes, they're nested classes. A child class inherits from its parent class, a nested class is just defined within the scope of its enclosing class. Very different concepts.

Now, the first thing I should point out is that since sent2 and sent3 are exactly identical, there is no need do define that class twice, you just make whatever instances you need. I should also point out that you never actually do keep hold of an instance of either of these identical classes, so the work in the __init__ function accomplishes nothing.
I am also not entirely clear on when you expect the setter function to be called, but I don't think it happens when you think it does. I think you might want to take a closer look:

In [1571]: c = sent()
c = sent()

In [1572]: c.subj
c.subj
Out[1572]: 'b'

In [1573]: c._subj
c._subj
Out[1573]: 'b'

In [1574]: setattr(c, "subj", "e")
setattr(c, "subj", "e")

In [1575]: c.subj
c.subj
Out[1575]: 'e' 

In [1576]: c._subj
c._subj
Out[1576]: 'b'  # uh oh!

In [1577]:


hint: setattr is a pretty blunt instrument. It just sets the attribute. So instead of setting the underlying property, you've actually nuked the subj function and replaced it with the string 'e'. Which could be awkward:

In [1577]: c.subj = "q"
c.subj = "q"

In [1578]: c.subj
c.subj
Out[1578]: 'q'

In [1579]: c._subj
c._subj
Out[1579]: 'b'


And sadness reigns.

Not that it matters, because without instances, setting instance fields isn't really getting you anything.

So yeah, it'd be really nice if you were to test your examples and try to make them demonstrate the sort of behavior you're asking about. It's not enough to provide a code-shaped object, go the extra mile and try to see it doing something. I mean, I'm pretty sure this is a terrible idea, but it's hard to make the case for that based on what you're showing us. Or rather, there are too many ways in which it's a terrible idea - if you get your example right, I could just limit myself to the terrible idea in your example.
Was This Post Helpful? 2
  • +
  • -

#3 bobsmith76   User is offline

  • D.I.C Regular

Reputation: 11
  • View blog
  • Posts: 343
  • Joined: 14-February 17

Re: make it so that when you change one attribute you change another

Posted 31 December 2018 - 12:28 AM

About the terminology, I thought I had referred to them as nested classes in my OP but I guess I forgot to do that. Second, in the real situation sent2 and sent3 will not be identical. I guess I should state why they are different. `sent2` iterates over all of the letters in a sentence whereas `sent3` iterates over just the variables. I realized you can easily make that distinction without having to make a separate class but it helps me to keep things neat and clean. Third, I don't see why anyone would use the code:


In [1579]: c._subj
c._subj
Out[1579]: 'b'


If you want to avoid `subj` outputting 'b' then just don't write `c._subj`. Seems easy enough. So I really don't see the drawbacks of your example. I would never use `c.subj` anyway, so I don't see how it would become a problem. Fourth, what's your counterproposal?

This post has been edited by ndc85430: 31 December 2018 - 12:29 AM
Reason for edit:: Removed quote of previous post.

Was This Post Helpful? 0
  • +
  • -

#4 ndc85430   User is offline

  • I think you'll find it's "Dr"
  • member icon

Reputation: 934
  • View blog
  • Posts: 3,741
  • Joined: 13-June 14

Re: make it so that when you change one attribute you change another

Posted 31 December 2018 - 12:36 AM

The point Jon is making is that with the call to setattr in your post, you're overwriting the subj method with a value that isn't a function, which is likely not what you want.

Perhaps the following example is illuminating:

>>> class Foo:
...     def bar(self):
...             pass
... 
>>> f = Foo()
>>> f.bar
<bound method Foo.bar of <__main__.Foo object at 0x7fae9190f668>>
>>> setattr(f, "bar", 3)
>>> f.bar
3
>>> 



Note the difference between lines 7 and 10 - after the call to setattr, f.bar is assigned the value 3 not the method any more. As such, the call f.bar() will fail.

This post has been edited by ndc85430: 31 December 2018 - 12:50 AM

Was This Post Helpful? 1
  • +
  • -

#5 bobsmith76   User is offline

  • D.I.C Regular

Reputation: 11
  • View blog
  • Posts: 343
  • Joined: 14-February 17

Re: make it so that when you change one attribute you change another

Posted 31 December 2018 - 01:33 AM

No, I don't want subj to be a method, I want it to be an attribute which is what it is in my example. The example does exactly what I want it to do so I don't see what the problem is. I'm just trying to find the best way to link two attributes in two different nested classes, such that when you change the attribute of one you automatically change the attribute of the other.
Was This Post Helpful? 0
  • +
  • -

#6 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7361
  • View blog
  • Posts: 15,284
  • Joined: 16-October 07

Re: make it so that when you change one attribute you change another

Posted 31 December 2018 - 06:31 AM

View Postbobsmith76, on 31 December 2018 - 03:33 AM, said:

No, I don't want subj to be a method,

Why, exactly? You clearly want it to behave as a method.

View Postbobsmith76, on 30 December 2018 - 10:24 PM, said:

When you change the attribute of the parent class, you automatically change the attributes of two child classes.

Indeed, this would be a good place for a method. Though, having the child know anything about the parent is bad design, in general.

View Postbobsmith76, on 30 December 2018 - 10:24 PM, said:

I realize some people might want me to use inheritance but I prefer nested classes to inheritance.

Excellent. Inheritance tends to be overemphasized as it is seen as a defining characteristic in OOP. I tend to see polymorphism as the major gain and in duck typed languages like Python, you basically get that for free.

View Postbobsmith76, on 30 December 2018 - 10:24 PM, said:

I don't like cluttering up my namespace with attributes that I don't need and nested classes allow me to do that, so just indulge me here, ok?

Again, why? Your intuition here is good, multitudes of attributes can make for messy, hard to follow, code. However, if you're designing with the intent of using a legion of attributes then perhaps it's the design that needs considering.

View Postbobsmith76, on 30 December 2018 - 10:24 PM, said:

I used `setattr` because eventually I'm going to be looping through a list of attributes. Let me know if you think this is the best way of doing things.

Using setattr at all is a bit of a hack. It breaks that polymorphic thing and requires the user of the class to inject functionality that should already explicitly be available to them.

View Postbobsmith76, on 31 December 2018 - 03:33 AM, said:

The example does exactly what I want it to do so I don't see what the problem is.

Does it? Interesting. Perhaps you need to give a little more code to show what this is offering you?

In fact, I don't believe it really does what you think, though this has been pointed out, I'll try again.
def trace(*args):
    trace.counter += 1
    print("{0}. {1}".format(trace.counter, ", ".join(str(x) for x in args)))
trace.counter = 0

class sent:
    def __init__(self):
        trace("send init")
        self.subj = "b"
        self.sent2().main(self) # this functionally does nothing

    @property
    def subj(self):
        trace("send subj property")
        return self._subj

    @subj.setter
    def subj(self, value):
        trace("send subj setter")
        self._subj = value
        self.sent2.subj = value

    class sent2:
        def __init__(self):
            trace("send2 init2", dir(self))
        def main(self, cls):
            trace("send2 main")
            self.subj = cls.subj


c = sent()
trace("c.subj", c.subj)
trace("c.sent2.subj", c.sent2.subj)
trace("dir(c.sent2)", dir(c.sent2))


trace('setattr(c, "subj", "e")')
setattr(c, "subj", "e")

trace("c.subj", c.subj)
trace("c.sent2.subj", c.sent2.subj)



Result:
1. send init
2. send subj setter
3. send2 init2, ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'main', 'subj']
4. send2 main
5. send subj property
6. send subj property
7. c.subj, b
8. c.sent2.subj, b
9. dir(c.sent2), ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'main', 'subj']
10. setattr(c, "subj", "e")
11. send subj setter
12. send subj property
13. c.subj, e
14. c.sent2.subj, e



Note, you'll get same final output if you never call self.sent2().main(self). This is because you're adding a property onto the class, not actually an instance of the class. Similar to my trace function, actually.

I believe, maybe, you might want something like:
def trace(*args):
    trace.counter += 1
    print("{0}. {1}".format(trace.counter, ", ".join(str(x) for x in args)))
trace.counter = 0

class SmartChild(object):
    def __init__(self, parent):
        self.parent = parent
    def __getattr__(self, name):
        return getattr(self.parent, name)

class Sent(object):
    def __init__(self):
        trace("send init")
        self.subj = "b"
        self.sent2 = SmartChild(self)

c = Sent()
trace("c.subj", c.subj)
trace("c.sent2.subj", c.sent2.subj)
trace("dir(c.sent2)", dir(c.sent2))

trace('setattr(c, "subj", "e")')
setattr(c, "subj", "e")

trace("c.subj", c.subj)
trace("c.sent2.subj", c.sent2.subj)


Here, we maintain a reference to our parent and bubble up the getattr call to that parent if we don't have an instance. This does have the property that we can override a parent's value locally.
Was This Post Helpful? 2
  • +
  • -

#7 jon.kiparsky   User is offline

  • Beginner
  • member icon


Reputation: 11374
  • View blog
  • Posts: 19,406
  • Joined: 19-March 11

Re: make it so that when you change one attribute you change another

Posted 31 December 2018 - 06:46 AM

View Postbobsmith76, on 31 December 2018 - 02:28 AM, said:

Fourth, what's your counterproposal?


That is most definitively not my problem, at least not until you've provided working code that does what you want. Since your example does not do anything that you want, and you clearly haven't tried it, my problem at this point is to get you to show us what you're actually trying to accomplish.

Come on, this is basic esr.

Quote

If you want to avoid `subj` outputting 'b' then just don't write `c._subj`. Seems easy enough.


The point is that the function that you thought was going to solve your problem - the function that you took the trouble to write, and the one that you were pinning your hopes to, the one that was going to keep your nested classes in sync with your enclosing class - is gone for good once you call setattr. Blown away in its prime, so sad.

Not that it was doing you any good, as you'd have noticed if you'd tested your example before posting it, because you never have an persistent instance of your nested classes, so setting instance variables on them is sort of silly. Which is in itself sort of irrelevant because you didn't even get that right - self.sent2.subj = value attempts to set an attribute on the class, where self.sent2().main(self) is creating an instance, asking it to set a value on itself, and then discarding it. You'll notice that in the latter case, there is literally no way that could do what you want. It's not a typo, it's not code that you've edited from some working code, this could literally never do anything like what you say you want.*

EDIT: also, the point of looking at the setattr call at all, from my point of view, was to see if this ever worked in your intended use case, which was to loop over some stuff and use setattr to trigger your subj function. This, as you can see, did not work as intended. As you would have seen, had you bothered to try out your example before posting it.

You've been at this a while. Please start learning from your mistakes. If you take the trouble to exercise your code before posting it, you'll save yourself and everyone else a lot of trouble, and we can get down to brass tacks - why this is a lousy idea in the first place - so much quicker.


* well, okay, if your mainfunctions had returned self, then you would at least have had a chance of catching the instance in __init__, but the implications of that are too horrible to bear thinking about.
Was This Post Helpful? 1
  • +
  • -

#8 bobsmith76   User is offline

  • D.I.C Regular

Reputation: 11
  • View blog
  • Posts: 343
  • Joined: 14-February 17

Re: make it so that when you change one attribute you change another

Posted 01 January 2019 - 12:22 PM

I've decided that this code was a bad idea. But still there are some things Jon is saying that I'm not understanding.

Quote

The point is that the function that you thought was going to solve your problem - the function that you took the trouble to write, and the one that you were pinning your hopes to, the one that was going to keep your nested classes in sync with your enclosing class - is gone for good once you call setattr. Blown away in its prime, so sad.


First, maybe there is some misunderstanding here about terminology because I thought any def statement inside a class was a method, but maybe when you write @property over it it become a function. If that's wrong then I don't see what function you're referring to. Second, I don't see what gets blown away after the setattr statement. Using the code below I want the assert statements to be true and they are.

class sent:
    def __init__(self):
        self.subj = "b"
        self.sent2().main(self)
        self.sent3().main(self)

    @property
    def subj(self):
        return self._subj

    @subj.setter
    def subj(self, value):
        self._subj = value
        self.sent2.subj = value
        self.sent3.subj = value

    class sent2:
        def main(self, cls):
            self.subj = cls.subj

    class sent3:
        def main(self, cls):
            self.subj = cls.subj


c = sent()
setattr(c, "subj", "e")
c.subj = "f"
assert c.sent2.subj == 'f'
assert c.sent3.subj == 'f'
c.subj = 'g'
assert c.sent2.subj == 'g'
assert c.sent3.subj == 'g'


So I don't see what is getting blown away.

In any case, the central issue here is: how do you loop over the attributes of the same class in two different ways. I guess I was just getting carried away with the __iter__ magic method and since as far as I'm aware of, it can only be used once, I decided to attach two nested classes onto a class and create a separate __itere method for both of them. That's probably taking things a bit too far. So I've decided to just tack a list of attributes onto the class that specifies which attributes I want to iterate over given a certain situation.

Quote

had you bothered to try out your example before posting it

This is simply not true.
Was This Post Helpful? 0
  • +
  • -

#9 bobsmith76   User is offline

  • D.I.C Regular

Reputation: 11
  • View blog
  • Posts: 343
  • Joined: 14-February 17

Re: make it so that when you change one attribute you change another

Posted 01 January 2019 - 12:32 PM

Hi Baavgai,
Thank you for your comments.

View Postbaavgai, on 31 December 2018 - 01:31 PM, said:

View Postbobsmith76, on 31 December 2018 - 03:33 AM, said:

No, I don't want subj to be a method,

Why, exactly? You clearly want it to behave as a method.

See the above post. Maybe I was not aware that when you add the @propertythat turns an attribute into a method.


Quote

View Postbobsmith76, on 30 December 2018 - 10:24 PM, said:

When you change the attribute of the parent class, you automatically change the attributes of two child classes.

Indeed, this would be a good place for a method. Though, having the child know anything about the parent is bad design, in general.

Could you point me to an example? Thanks.


Quote

View Postbobsmith76, on 30 December 2018 - 10:24 PM, said:

I realize some people might want me to use inheritance but I prefer nested classes to inheritance.

Excellent. Inheritance tends to be overemphasized as it is seen as a defining characteristic in OOP. I tend to see polymorphism as the major gain and in duck typed languages like Python, you basically get that for free.

Good, glad we agree on that.

Quote

View Postbobsmith76, on 30 December 2018 - 10:24 PM, said:

I don't like cluttering up my namespace with attributes that I don't need and nested classes allow me to do that, so just indulge me here, ok?

Again, why? Your intuition here is good, multitudes of attributes can make for messy, hard to follow, code. However, if you're designing with the intent of using a legion of attributes then perhaps it's the design that needs considering.

I think you already answered your own question: "multitudes of attributes can make for messy, hard to follow, code." In general, I try to keep the number of active attributes to below 20 but if I can get it down to less than 10 then so much the better.

Quote

View Postbobsmith76, on 30 December 2018 - 10:24 PM, said:

I used `setattr` because eventually I'm going to be looping through a list of attributes. Let me know if you think this is the best way of doing things.

Using setattr at all is a bit of a hack. It breaks that polymorphic thing and requires the user of the class to inject functionality that should already explicitly be available to them.

Well, what about when you're iterating over a set of attributes and need to change them? I've always used the setattr built-in to accomplish that til now.

Quote

View Postbobsmith76, on 31 December 2018 - 03:33 AM, said:

The example does exactly what I want it to do so I don't see what the problem is.

Does it? Interesting. Perhaps you need to give a little more code to show what this is offering you?

I've already changed my mind about the code, so I can't do that.
Was This Post Helpful? 0
  • +
  • -

#10 ndc85430   User is offline

  • I think you'll find it's "Dr"
  • member icon

Reputation: 934
  • View blog
  • Posts: 3,741
  • Joined: 13-June 14

Re: make it so that when you change one attribute you change another

Posted 01 January 2019 - 12:45 PM

View Postbobsmith76, on 01 January 2019 - 07:22 PM, said:

In any case, the central issue here is: how do you loop over the attributes of the same class in two different ways.


We seem to be in the depths of one of your seemingly over complicated solutions again. Maybe wanting to do something like this is sane, but that many of your questions here have to resort to things like adding/removing attributes at runtime, type checking and other strange things really makes one think that your software could be designed in a better way. The problem is, though, that what we're presented with is the solution you've chosen, rather than the problem you're trying to solve and that makes it difficult to suggest appropriate approaches. It took several posts in this thread, for example, for us to learn that you wanted to make sure that your software still worked after refactoring. There are a number of experienced programmers here who can help you to do things sensibly, so please, use their time (and yours) wisely and talk at a higher level.
Was This Post Helpful? 1
  • +
  • -

#11 jon.kiparsky   User is offline

  • Beginner
  • member icon


Reputation: 11374
  • View blog
  • Posts: 19,406
  • Joined: 19-March 11

Re: make it so that when you change one attribute you change another

Posted 01 January 2019 - 02:17 PM

Quote

I've decided that this code was a bad idea


Oh, so that was what that chorus of angels was singing "hallelujah" about. Verg good.

Quote

I don't see what is getting blown away.


The function (aka method - if you're making a distinction, it's probably not a helpful one) called subj and decorated as a "setter") is an attribute of the class sent. When you call setattr(instance, attr_name, new_value), it will set the value of instance.attr_name to whatever new_value turns out to be. That is, the function called subj is wiped away and replaced with a string.

As I say, this is probably not the behavior that you want.

Fortunately, you've turned away from this dark path and we need spend no more time on this matter.

I would recommend, though, that you spend a few days meditating on the distinction between instance and class. This is important.
Was This Post Helpful? 1
  • +
  • -

#12 bobsmith76   User is offline

  • D.I.C Regular

Reputation: 11
  • View blog
  • Posts: 343
  • Joined: 14-February 17

Re: make it so that when you change one attribute you change another

Posted 01 January 2019 - 05:30 PM

View Postjon.kiparsky, on 01 January 2019 - 09:17 PM, said:

I would recommend, though, that you spend a few days meditating on the distinction between instance and class. This is important.


Maybe the confusion lies in the fact that about 75% of the time I don't use classes which will instantiate several objects, rather I use classes so as to take advantage of the feature that you can see its attributes' values quite easily in the IDE editor. In other words, most of my classes only instantiate one object and they are not used again. I don't know the name that the gang of four calls these classes. So for me, classes are mostly used as mini-modules that just bundle together a set of functions and are generally only used once during the execution of the code.

View Postndc85430, on 01 January 2019 - 07:45 PM, said:

makes one think that your software could be designed in a better way.


Well, if you're interested in reading 7000 lines of code and learning a new predicate calculus, then I'd be more than happy to learn some new tricks of the trade from a certified master.
Just send me a PM.

View Postjon.kiparsky, on 01 January 2019 - 09:17 PM, said:

the function called subj is wiped away and replaced with a string.


Well, then why is it the case that lines 7 and 8 did not fail?
c = sent()
setattr(c, "subj", "e")
c.subj = "f"
assert c.sent2.subj == 'f'
assert c.sent3.subj == 'f'
c.subj = 'g'
assert c.sent2.subj == 'g'
assert c.sent3.subj == 'g'


Was This Post Helpful? 0
  • +
  • -

#13 modi123_1   User is offline

  • Suitor #2
  • member icon



Reputation: 14687
  • View blog
  • Posts: 58,690
  • Joined: 12-June 08

Re: make it so that when you change one attribute you change another

Posted 01 January 2019 - 05:33 PM

Quote

rather I use classes so as to take advantage of the feature that you can see its attributes' values quite easily in the IDE editor.

To be clear you are not using classes for architecture and design, but for ease of use of an IDE? :blink:
Was This Post Helpful? 1
  • +
  • -

#14 bobsmith76   User is offline

  • D.I.C Regular

Reputation: 11
  • View blog
  • Posts: 343
  • Joined: 14-February 17

Re: make it so that when you change one attribute you change another

Posted 01 January 2019 - 05:48 PM

View Postmodi123_1, on 02 January 2019 - 12:33 AM, said:

Quote

rather I use classes so as to take advantage of the feature that you can see its attributes' values quite easily in the IDE editor.

To be clear you are not using classes for architecture and design, but for ease of use of an IDE? :blink:/>

I'm using them for ease of use.

View Postndc85430, on 01 January 2019 - 07:45 PM, said:

makes one think that your software could be designed in a better way.


Actually, you don't have to read the whole code before you're able to give expert advice on how it can be improved. You can do that after reading the first module.
Was This Post Helpful? 0
  • +
  • -

#15 jon.kiparsky   User is offline

  • Beginner
  • member icon


Reputation: 11374
  • View blog
  • Posts: 19,406
  • Joined: 19-March 11

Re: make it so that when you change one attribute you change another

Posted 01 January 2019 - 06:24 PM

View Postbobsmith76, on 01 January 2019 - 07:30 PM, said:

Maybe the confusion lies in the fact that about 75% of the time I don't use classes which will instantiate several objects, rather I use classes so as to take advantage of the feature that you can see its attributes' values quite easily in the IDE editor.


So basically, you're out to obfuscate your code because you don't know how to use your tools correctly?
Color me done.
Was This Post Helpful? 1
  • +
  • -

  • (2 Pages)
  • +
  • 1
  • 2