I'm lost and confused (Veneer Art Project) Python

I don’t understand two things:

def sell_artwork(self, artwork, price):
    if artwork.owner == self:           **<----------------1)  owner is a part of the Art class not Client class so how can we use artwork.owner here? Without Inheritance etc?**
      new_listing = Listing(artwork, price, self)       
      veneer.add_listing(new_listing)                   

  def buy_artwork(self, artwork):
    if artwork.owner != self:           
      art_listing = None
      for listing in veneer.listings:
        if listing.art == artwork:          **<----------------- 2) Same thing here veneer.listing is a part of the Marketplace class which doesn't have an art parameter, so how is listing.art there?**
          art_listing = listing         
          break
      if art_listing != None:
        art_listing.art.owner = self    
        veneer.remove_listing(art_listing)

Full code

class Art:
  def __init__(self, artist, title, medium, year, owner):
    self.artist = artist
    self.title = title
    self.medium = medium
    self.year = year
    self.owner = owner

  def __repr__(self):
    return f"{self.artist}. {self.title}. {self.year}, {self.medium}. {self.owner.name}, {self.owner.location}." 

class Marketplace:
  def __init__(self):
    self.listings = []

  def add_listing(self, new_listing):
    self.listings.append(new_listing)

  def remove_listing(self, ex_listing):
    self.listings.remove(ex_listing)

  def show_listings(self):
    for listing in self.listings:
      print(listing)


class Client:
  def __init__(self, name, location, is_museum):
    self.name = name
    self.is_museum = is_museum
    if is_museum:
      self.location = location
    else:
      self.location = "Private Collection"


  def sell_artwork(self, artwork, price):
    if artwork.owner == self: 
      new_listing = Listing(artwork, price, self)       
      veneer.add_listing(new_listing)                   

  def buy_artwork(self, artwork):
    if artwork.owner != self:           
      art_listing = None
      for listing in veneer.listings:
        if listing.art == artwork:
          art_listing = listing         
          break
      if art_listing != None:
        art_listing.art.owner = self    
        veneer.remove_listing(art_listing)

class Listing:
  def __init__(self, art, price, seller):
    self.art = art
    self.price = price
    self.seller = seller

  def __repr__(self):
    return f"{self.art.title}, {self.price}"


veneer = Marketplace()

veneer.show_listings()

edytta = Client("Edytta Halpirt", None, False)
moma = Client("The MOMA", "New York", True)

girl_with_mandolin = Art("Picasso, Pablo", "Girl with a Mandolin (Fanny Tellier)", "oil on canvas", 1910, moma)
mc = Art("M.C.Escher", "Infinite Stairwell", "oil on canvas", 1895, edytta) 


moma.sell_artwork(girl_with_mandolin, "$6 million (USD)")

veneer.show_listings()

edytta.buy_artwork(girl_with_mandolin)
print(girl_with_mandolin)
print(mc)

print(veneer.show_listings())

Welcome to the forums!

  1. Yes, artwork is an instance of the Art class, but we can still access its instance variables outside of the class. These instance variables are attached to the object (artwork) itself, not the class to which the object belongs to. Here, we are checking if the Client on which we called .sell_artwork() is the same client that is the owner of the art piece.
  2. Again, we can access instance variables outside of the class to which their respective object belongs to.

The following resources might be helpful in understanding class and instance variables (both of which we can access outside of their classes depending on their “access modifiers” [in quotations because Python doesn’t really have concepts of private or protected]).

I understand all the other code except for literally just the snippet of code I referenced. Thanks for the articles, but I am still just as confused as before.

For your first question, when you call the sell_artwork method, one of the parameters you pass in is an Art object. This means you have access to the public methods and properties of whatever Art object you put in. That’s what lets you access the .owner property of the Art from within the Client class.
It’s similar to if you created a function that takes a string as a parameter. Each string is its own instance of an object, but within the function you are able to use string methods even though they aren’t part of your own classes and methods.

As for your second question, when you create a Marketplace, it creates a list of Listings. By iterating through the list, you are able to look at each Listing and their properties.
Using strings as an example again, if you have a list of strings, you can go through the list and use their methods and properties.

To sum up my previous post in one sentence: even though artwork.owner is “in” in the Art class and veneer.listings is “in” the Marketplace class, they can be accessed outside of their respective classes; this means inheritance is not needed.