Python Forum
Understanding Python classes
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Understanding Python classes
#1
Hi People

Please help me understand Python classes a bit better.
This code does not do what I want it to do, which is changing the value of class A's member variable.
I do not understand why it is behaving the way it is. Can someone educate me?
class A:
    def __init__(self,a,b):
        self.a_=a 
        self.b_=b
        print(self.__repr__())

    def __repr__(self):
        return f'A(a={self.a_},b={self.b_})'

    def set_a(self,a):
        print(f'executing A.set() with a value of {a} passed')
        self.a_ = a


class B:
    def __init__(self,c):
        self.c_ = c 
        print(self.__repr__())

    def __repr__(self):
        return f'B(c={self.c_})'

    def setboth(self,ss):
        print(f'executing B.setboth with {ss} passed')
        self.c_ = ss
        print(f'just set B.c_ to {ss}')
        print(f'about to call A.set_a({ss}) from B.setboth()')
        A.set_a(ss)

def main():
    AA = A(1,2)
    BB = B(12)
    print('setting A.a_ to 13')
    AA.set_a(13)
    AA.__repr__()

    print('setting B.c_=23 and A.a_=23')
    BB.setboth(ss=23)
    print(AA.__repr__())
    print(BB.__repr__())

main()
I expected the value of A.a_ to be changed to 23, but that is not happening??
Reply
#2
Look a the signature for set_a(). It expects two arguments; an instance of class A and a value.
def set_a(self,a):
This code is trying to call an instance method of class A, but it cannot because it doesn't have an instance of class A.
    def setboth(self,ss):
        print(f'executing B.setboth with {ss} passed')
        self.c_ = ss
        print(f'just set B.c_ to {ss}')
        print(f'about to call A.set_a({ss}) from B.setboth()')
        A.set_a(ss)  # Cannot do this.  Need to do instance_of_a.set_a(ss) or A.set_a(instance_of_a, ss)
Maybe you are trying to do something like this:
class A:
    def __init__(self,a,b):
        self.a = a 
        self.b = b
 
    def set_a(self,a):
        self.a = a

    def __repr__(self):
        return f'A(a={self.a},b={self.b})'

 
class HasA:
    # I am an aggregate class.  I contain instances of other classes that I use.
    def __init__(self, a=None):
        self.a = A(0, 0) if a is None else a
 
    def set_a(self, value):
        self.a.set_a(value)
 
    def __repr__(self):
        return f'HasA(a={self.a})'

b = HasA()
print(b)
b.set_a(42)
print(b.a)
Output:
HasA(a=A(a=0,b=0)) A(a=42,b=0)
How about you explain what you are trying to do so we can help you decide if you need classes, and if you need classes, how the classes should be designed. You already tried is-a (b "is a" sublclass of a). I just showed you a "has-a" (b "has a(n)" instance of a). Maybe you need "uses" where the two classes are completely unrelated, but A provides an interface that B knows how to use.
Reply
#3
(Nov-09-2022, 05:02 PM)deanhystad Wrote: Look a the signature for set_a(). It expects two arguments; an instance of class A and a value.
def set_a(self,a):
This code is trying to call an instance method of class A, but it cannot because it doesn't have an instance of class A.
    def setboth(self,ss):
        print(f'executing B.setboth with {ss} passed')
        self.c_ = ss
        print(f'just set B.c_ to {ss}')
        print(f'about to call A.set_a({ss}) from B.setboth()')
        A.set_a(ss)  # Cannot do this.  Need to do instance_of_a.set_a(ss) or A.set_a(instance_of_a, ss)
Maybe you are trying to do something like this:
class A:
    def __init__(self,a,b):
        self.a = a 
        self.b = b
 
    def set_a(self,a):
        self.a = a

    def __repr__(self):
        return f'A(a={self.a},b={self.b})'

 
class HasA:
    # I am an aggregate class.  I contain instances of other classes that I use.
    def __init__(self, a=None):
        self.a = A(0, 0) if a is None else a
 
    def set_a(self, value):
        self.a.set_a(value)
 
    def __repr__(self):
        return f'HasA(a={self.a})'

b = HasA()
print(b)
b.set_a(42)
print(b.a)
Output:
HasA(a=A(a=0,b=0)) A(a=42,b=0)
How about you explain what you are trying to do so we can help you decide if you need classes, and if you need classes, how the classes should be designed. You already tried is-a (b "is a" sublclass of a). I just showed you a "has-a" (b "has a(n)" instance of a). Maybe you need "uses" where the two classes are completely unrelated, but A provides an interface that B knows how to use.



Hi Dean

Sure. I'm studying the concepts around AI agents, and currently working on the concept of a table-driven agent. I'm trying to code the classic vacuum cleaner problem, where a vacuum cleaner inspects and cleans two rooms.
Conceptually I see an environment consisting of two rooms and a vacuum cleaner interacting with the environment.
So I see two classes in this. An Environment class and a vacuum class.

In order for my concept to work, the vacuum must update the status of a room once it is cleaned, and that lead to my question above. So the Environment class would have a "status" member and a "setstatus()" member function; which the vacuum class object would need to access in order to change the room status. Make sense?

So I managed to get it to work by changing my approach. I ended up using nested classes. Meaning I have an Environment class inside the Vacuum class. That is in essence what you demonstrated above with the HasA class.
However, the "uses" scenario is what I am after.
Can you explain how that would work?

I attached my .py file for reference.

Attached Files

.py   agent7.py (Size: 4.37 KB / Downloads: 63)
Reply
#4
"Uses" is a lot like "has-a" except the two classes don't have to know everything about each other, and the relationship may be temporary.

Here I make a Room class and a Vaccuum class. I build a map of rooms that the vaccuum can use to clean all the dirty rooms. The vaccuum doesn't know much about rooms other than clean(), dirty(), neighbors() and dirty_neighbors().
import random

class Room():
    """I am a room.  I have a name (id) and neighboring rooms"""
    def __init__(self, id, neighbors=None):
        self.id = id
        self.neighbors = neighbors if neighbors else []
        self.isclean = False

    def add_neighbor(self, neighbor):
        self.neighbors.append(neighbor)

    def clean(self):
        self.isclean = True

    def dirty(self):
        return not self.isclean

    def dirty_neighbors(self):
        return [neighbor for neighbor in self.neighbors if neighbor.dirty()]

    def __repr__(self):
        neighbors = [neighbor.id for neighbor in self.neighbors]
        return f"{self.id}, neighbors={neighbors}"

class Vaccuum():
    """I am a vaccuum.  I clean rooms."""
    def __init__(self):
        self.rooms = []

    def clean(self, rooms, start=None):
        self.rooms = rooms
        self.room = start if start else rooms[0]

    def next_room(self):
        dirty_rooms = [room for room in self.rooms.values() if room.dirty()]
        if dirty_rooms:
            for room in self.room.neighbors:
                if room in dirty_rooms:
                    self.room = room
                    room.clean()
                    return room
            else:
                self.room = random.choice(self.room.neighbors)
                return self.room
        return None

# Make a map of rooms
map = (
    ("Kitchen", "Dining Room", "Living Room", "Hall"),
    ("Dining Room", "Kitchen"),
    ("Living Room", "Kitchen", "Hall"),
    ("Hall", "Kitchen", "Living Room", "Bedroom", "Bath"),
    ("Bedroom", "Hall"),
    ("Bath", "Hall")
)

rooms = {room[0]:Room(room[0]) for room in map}
for room in map:
    for neighbor in room[1:]:
        rooms[room[0]].add_neighbor(rooms[neighbor])

print("This is the map")
for room in rooms.values():
    print(room)

print("This is the route")
vaccuum = Vaccuum()
vaccuum.clean(rooms, rooms['Bath'])
while (room := vaccuum.next_room()):
    print(room.id)
Output:
This is the map Kitchen, neighbors=['Dining Room', 'Living Room', 'Hall'] Dining Room, neighbors=['Kitchen'] Living Room, neighbors=['Kitchen', 'Hall'] Hall, neighbors=['Kitchen', 'Living Room', 'Bedroom', 'Bath'] Bedroom, neighbors=['Hall'] Bath, neighbors=['Hall'] This is the route Hall Kitchen Dining Room Kitchen Living Room Hall Bedroom Hall Bath
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Python Classes rob101 4 542 Feb-05-2024, 06:51 PM
Last Post: rob101
  Understanding venv; How do I ensure my python script uses the environment every time? Calab 1 2,294 May-10-2023, 02:13 PM
Last Post: Calab
  New to python/coding Need help on Understanding why this code isn't working. Thanks! mat3372 8 1,761 May-09-2023, 08:47 AM
Last Post: buran
Sad Python classes PythonNewbee 4 1,057 Nov-09-2022, 01:19 PM
Last Post: deanhystad
  I need help understanding a program structure using classes CompleteNewb 21 6,066 Feb-24-2022, 07:15 PM
Last Post: deanhystad
  Inheritance vs Instantiation for Python classes mr_byte31 7 2,893 Oct-14-2021, 12:58 PM
Last Post: mr_byte31
  Understanding Python super() for classes OmegaRed94 1 1,840 Jun-09-2021, 09:02 AM
Last Post: buran
  Better Understanding Of Object Orientation In Python JoeDainton123 3 2,487 Aug-30-2020, 02:49 PM
Last Post: deanhystad
  Python classes Python_User 15 4,895 Aug-04-2020, 06:57 PM
Last Post: Python_User
  Understanding Python's Import Engine MysticaL 1 2,176 Feb-07-2020, 11:26 PM
Last Post: snippsat

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020