Welcome to the Treehouse Community
The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.
Property Setter Question
For this exercise, we're asked to build a setter for the price property by changing "_price". The exercise provides the following code:
class Product: _price = 0.0 tax_rate = 0.12 def __init__(self, base_price): self._price = base_price @property def price(self): return self._price + (self._price * self.tax_rate)
I'm wondering why this is an answer that the exercise 'Check Work' accepts:
@price.setter def price(self, new_price): self._price = new_price
Instead of this...
@price.setter def price(self, new_price): self._price = new_price/(1 + tax_rate)
Isn't the exercise checker wrong? I figured when a user types "[Product instance].price = 95", that the user is assigning 95 to 'price' ( calculated by self._price + (self._price * self.tax_rate) ) and not '_price' (which is just the base price)
Okay, so we are using setter to set the value of _price, which we cannot do like a normal member.
You can either:
#As the question say: "Add a new setter (@price.setter) method to the Product class that updates the _price attribute." @price.setter def price(self, new_price): #replace price with new price. self._price = new_price
and update the price or you could do whatever manipulations you required to make.
But as mentioned in the question, here we just have to set the new price as passed in the arg.
Hi, Chris- good to hear from ya!
I think that whatever you do in the "getter", you sorta have to do the opposite of in the "setter" for these type of exercises. If you look at the Special Methods video (at 6:35), Kenneth essentially has self.radius = self.diameter/2 in the getter.... in the setter he has self.diameter = new_radius * 2. The getter has divided by 2 and the setter has multiplied by 2... which makes sense as the first one is assigning the radius and the other is assigning the diameter.
Likewise, in this exercise, the getter is essentially calculating self.price = self._price * (1 + self.tax_rate), so the setter must be self._price = self.price/(1 + self.tax_rate). One is multiplication and the other is division based on what we are assigning. This is because the getter includes tax, so the setter must exclude tax when assigning the _price variable. We could run a test of the code in the IDLE. For example, consider the following:
If you were to run this code....
p = Product(5) #New instance p.price = 6 #Reassigning price property p.price #asking the IDLE to evaluate the price property we just reassigned
in both of the Scenarios shown below, then you'll see that the As-IS solution [that is said to be correct] actually gives 6.72. Huh?? I thought we just set p.price = 6... why is Python telling us it is 6.72? Oops!!!! Because we didn't discount _price by the tax rate so that when the getter was called, it would factor in the tax rate and give us back 6.
BUT, if you evaluate p.price in Scenario 2, it will give 6. This is consistent with what Kenneth did in the video, and what the average user would have intended when typing "p.price = 6". After all, we want p.price to equal 6... not 6.72. Which is why I think the Setter exercise (either in the solution that the checker deems as right or by revising the instructions) may need to be revised. Hence the reason for my post... an attempt to be helpful... which has turned out to be an essay : ) Thanks, D.
Scenario 1: The As-Is price-setter Solution that the checker accepts
#[rest of code in exercise here] @price.setter def price(self, new_price): self._price = new_price
Scenario 2: The proposed price.setter Solution that the checker rejects (yet I think this is actually the correct answer and Scenario 1 is wrong)
#[rest of code in exercise here] @price.setter def price(self, new_price): self._price = new_price/(1 + self.tax_rate)
Thanks, Chris- good point about teaching us students that a setter and getter can definitely be different from the pattern shown in the video. You know, I wish I had a suggestion for just a wording change (...that would be easier to revise, I'm sure), but I can't think of one at the moment that really does the trick.
As-is, the current wording "We need to be able to set the price of a product through a property setter. Add a new setter (@price.setter) method to the Product class that updates the _price attribute" tells me that the objective is to set price by updating _price using a property setter. (Besides, you wouldn't need a setter if you're ultimate goal was just to update _price, but you would if your ultimate goal was to update "price" by using _price.) So that's one of the reasons I likened it to setting the radius (i.e. price) by changing the diameter (i.e. _price) in Kenneth's video.
In a perfect world, I would recommend changing the solution and adding to the wording of the exercise "Don't forget that the getter adds in tax, so account for that in your setter!" Then Kenneth could make some pun like 'no taxation without representation' and we'd be golden! : )
Tatiane Lima6,738 Points
Here is what i did:
@price.setter def price(self, price): self._price = price / round(self.tax_rate + 1)
Thomas Ross4,634 Points
I got very stuck on this one, as well, and it was very frustrating. I'm glad I found this post, but was a bit disappointed with the solution. When this program is working correctly, here is what the user will experience:
>>> cog = Product(15) >>> cog.price 16.8 >>> cog._price 15 >>> cog.price = 16.8 >>> cog.price 18.816000000000003
price the user has just set is completely different than the
price the user has just asked for. This is incredibly confusing.
The solution to this should be something like
@price.setter def price(self, new_price): self._price = new_price / (1 + self.tax_rate)
where we are assuring that
price is the total price that includes tax, and
_price stores the pre-tax price.
I'd like to cast a vote for including the tax in the setter since the getter includes it.
@price.setter def price(self, new_price): self._price = new_price + (new_price * self.tax_rate)