Veneer project - what is the difference between codes in .buy_artwork method?


#1

Me again! :slight_smile:
Question. I am currently on https://www.codecademy.com/paths/computer-science/tracks/cspath-cumulative-art-marketplace/modules/cspath-veneer/projects/veneer

And since there is this walkthrough, I wanted to compare my results and there is this piece of code that doesn’t let me sleep well. It is a method of the Client class (.buy_artwork() ).
In the video it is shown as:

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 "{}, \"{}\", {}, {}, {}, {}.".format(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, removed_listing):
    self.listings.remove(removed_listing)
  def show_listings(self):
    for item in self.listings:
      print(item)
  
class Client():
  def __init__(self, name, is_museum, location):
    self.name = name
    if is_museum == True:
      self.location = location
    else:
      self.location = "Private Collection"

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

  def buy_artwork(self, artwork):
    if artwork.owner != self:
      art_listing = None
      for listing in veneer.listings:
        if listing.art == artwork:
          art_listing = listing
          artwork.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 "{}, {}".format(self.art.title, self.price)
  

  
veneer = Marketplace()
print(veneer.show_listings())

edytta = Client("Edytta Halpirt", False, None)

moma = Client("The MOMA", True, "New York")

girl_with_mandolin = Art("Picasso, Pablo", "Girl with a Mandolin (Fanny Tellier)", 1910, "oil on canvas", edytta)
print(girl_with_mandolin)

edytta.sell_artwork(girl_with_mandolin, "$6M (USD)")

veneer.show_listings()

moma.buy_artwork(girl_with_mandolin)
print(girl_with_mandolin)
veneer.show_listings()

My version thou is:

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

It works but there is a reason for sure why the difference occurred. My question is: why probably am I wrong? :smiley:


#2

A method cannot know what names we will give to its class instances, that is why it always uses self as the context variable to identify the current owner instance of the method.


#3

Ahh! I pasted the wrong first code! Here is the right one from the walkthrough (it is different only in this particular fragment):

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)

And my question is now : what is so different about those two codes? Why there is a “break”?


#4

If, during one of the iterations of the for loop, the condition listing.art == artwork is True, then the artwork that the Client wishes to buy has been found in veneer.listings, meaning it is available to buy. At that point, the transaction can be completed, so there is no need for the loop to continue to iterate. Without the break, the loop would continue, which would not cause any harm, aside from the fact that it would be looking for something that had already been found. It is a matter of efficiency.


#6

Great! Thanks for the answers. :slight_smile:


#7
def buy_artwork(self,artwork): #artwork is an Art class
    if artwork.owner != self:
      art_listing = None
      for listing in veneer.listings:
        if listing.art == artwork:
          art_listing = listing
          break
      if art_listing != None:
        artwork.owner = self   #artwork.art.owner  I don't think you need "art" because artwork is already an art class so doesn't it automatically go there?       
        veneer.remove_listing(art_listing)

vs

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)

I can’t track the video’s version “art_listing.art.owner” . Can someone help trace the map to what where this leads? If that even makes sense?

He says " Remember that the owner is a property of art_listing.art not just art_listing" . I don’t understand this.


#8

Hi @monochromaticmau

Where does the variable art_listing get defined in the second code example?


#9

I updated the original post. I also started to comment which arguments are from classes. It’s helping me with the flow of information.

Edit: I found my code works just the same. Does his code just contain extra information “art” in art_listing.art.owner = self? I just tried it and i got an error. So how did the guy in the video make it work? Is this an error? Right after that the video seemed to jump.


#10

This line of the first listing …

        artwork.owner = self

… is equivalent to this line in the second listing …

        art_listing.art.owner = self

To make sense of this, note this if block …

        if listing.art == artwork:
          art_listing = listing

If the condition is True, these three expressions all wind up referring to the same Art object, which is the one that is to be bought …

  • listing.art
  • artwork
  • art_listing.art

So you can assign self, the new owner of the piece, to any of the following …

  • listing.art.owner
  • artwork.owner
  • art_listing.art.owner

… for example …

        art_listing.art.owner = self

EDITED on December 5, 2018 to clarify the effect of the above assignment statement.


#11

Ah! I understand. art_listing.art is equivalent to artwork. Thank you!