0

So this simple program should just check for existing owners of a property, current owners, the sale price and a few other things. I just learned a bit of oop last night and I'm wondering if there is a way to ignore or skip over certain positional arguments and let them default to variables in the class instead.

So in my "house2" instance down below, this represents a house that was just currently built so it doesn't have any current owners or previous owners.

Instead of entering None for the values I don't have,(previous owners, current owners) is there away where I can say, "hey skip positional arguments 'current owners' and 'previous owners' and just use the variables within the class instead". That way it would save me from typing None for every value that doesn't exist.

So my instance would look like this instead:

house2 = houseStats('77 Book Worm St', 'Inner-City', 1, 1, '120000')

compared to this:

house2 = houseStats('77 Book Worm St', 'Inner-City', 1, None, None, 1, '120000')

Full block of code below:

# A simple program to get information of a house.

class houseStats:
    # class variables to default to if there are no instance variables.
    current_owner = 0
    previous_owner = 0
    forsale = 0

    def __init__(self, address, area, houseAge, currentOwner, previousOwner, forSale, salePrice):
        self.address = address
        self.area = area
        self.house_age = houseAge
        self.current_owner = currentOwner
        self.previous_owner = previousOwner
        self.forsale = forSale
        self.saleprice = salePrice
        # Function to determine the house age

    def houseage(self):
        return f"This house is {self.house_age} years old"

    # Function to determine whether the house is for sale and who sold it.
    def sold(self):
        if self.forsale is None:
            print("House is currently not for sale..")
        else:
            print(f'House is currently for sale for ${int(self.saleprice)}')


house1 = houseStats('19 Galaxy Way', 'Suburbs', 5, 'Douglas Forword', None, 1, 10000)
house2 = houseStats('77 Book Worm St', 'Inner-City', 1, None, None, 1, '120000')

house1.sold()
  • Defaults have to be at the end. Consider using named arguments for functions like this--it's really hard to tell what each field corresponds to here. – ggorlen Feb 26 '21 at 18:50
  • Positional parameters should be used when those arguments are required. If they aren't required, they should be keyword arguments with a default which you can then handle however you like within the function. – TigerhawkT3 Feb 26 '21 at 18:52
  • To skip an argument you have to default it, such as `param=None`. In order to default an argument, there needs to NOT be any unDefaulted param after it. So move them to the end. – thethiny Feb 26 '21 at 19:00
  • Could I have an example as the form of an answer? I'm a little confused as to what it should look like. – Johnny Silverhand Feb 26 '21 at 19:08
  • See answer number 1 https://stackoverflow.com/questions/15535655/optional-arguments-in-initializer-of-python-class – pippo1980 Feb 26 '21 at 19:18

1 Answers1

0

Just to reinforce some of the points mentioned in the comments and to illustrate with the whole code - if you want to avoid typing None while constructing the objects, the best way is to simply move your currentOwner, previousOwner and forSale parameters at the end of the constructor and set them to None there:

def __init__(self, address, area, houseAge, currentOwner, previousOwner, forSale, salePrice):

to

def __init__(self, address, area, houseAge, salePrice, forSale=None, currentOwner=None, previousOwner=None):

This way if you do not pass any arguments to those parameters, Python will assume that by default their value is None. If you pass them an argument - None will get replaced by that argument.

You can test that by adding this line to your code:

class houseStats:
    def __init__(self, address, area, houseAge, salePrice, forSale=None, currentOwner=None, previousOwner=None):
    *** implement construction here***

    def __repr__(self):
        return f"Current owner is {self.current_owner} and previous owner is {self.previous_owner}"


house1 = houseStats('19 Galaxy Way', 'Suburbs', 5, 10000, 1)
house2 = houseStats('15 Milky Way', 'Somewhere', 1, 50000, 1, 'Firstname Lastname')
print(house1)  # Current owner is None and previous owner is None
print(house2)  # Current owner is Firstname Lastname and previous owner is None

You have to be careful with this though because they are still considered positional arguments and they are overwritten based on how you construct your objects. If you switch the places of 1 and 'Firstname Lastname' in the house2 object example, the output will change entirely.

You could potentially use *args instead but depending on the logic of your program this may become tricky.

The whole code:

class houseStats:
    def __init__(self, address, area, houseAge, salePrice, forSale=None, currentOwner=None, previousOwner=None):
        self.address = address
        self.area = area
        self.house_age = houseAge
        self.saleprice = salePrice
        self.forsale = forSale
        self.current_owner = currentOwner
        self.previous_owner = previousOwner

    # Function to determine the house age
    def houseage(self):
        return f"This house is {self.house_age} years old"

    # Function to determine whether the house is for sale and who sold it.
    def sold(self):
        if self.forsale is None:
            print("House is currently not for sale..")
        else:
            print(f'House is currently for sale for ${int(self.saleprice)}')

Note that you can change int(self.saleprice) to self.saleprice in the F string at the very end and it will still work. In case of a floating point price, you will receive the exact price (e.g 10000.99), although for house selling in a real life situation nobody asks you for those extra few cents.

Rafaelo
  • 13
  • 4