Imagine that you are developing software for a big shipping company (why would you imagine a small one anyway), and you got a task to create a function for calculating a charge for ships based on their cargo weight. Easy breezy:

WEIGHT_RATES = [
    ( 10, 10.55),
    (  5, 5.05),
    (  2, 3.35),
    (  0, 1.25)
]

def shipping_charge(weight):
    if weight < 0:
        raise ValueError("Can't calculate shipping charge of negative weights")
    for min_weight, rate in WEIGHT_RATES:
        if weight > min_weight:
            return weight * rate

Simple enough.

But then, one day, your program eventually is going to work in another country, say the USA. One problem arises: we need to use pounds instead of kilograms for charging. No problem, here you are:

def shipping_charge(weight, pnds):
    if pnds:
       weight /= 2.2
    if weight < 0:
       raise ValueError("Can't calculate shipping charge of negative weights")
    for min_weight, rate in WEIGHT_RATES:
        if weight > min_weight:
            return weight * rate

This is getting complicated, but then one more requirement — you need to raise an exception if weight exceeds 1,000 kilograms for specific directions:

def shipping_charge(weight, pnds, exceed):
    if pnds:
       weight /= 2.2
    if exceed and weight > 1000:
       raise Exception("Weight can't exceed 1000 kg")
    if weight < 0:
       raise ValueError("Can't calculate shipping charge of negative weights")
    for min_weight, rate in WEIGHT_RATES:
        if weight > min_weight:
            return weight * rate

You may also like: 10 Reasons to Learn Python in 2019.

Do you see the problem? In this example, you came up to function with three positional arguments, two of them with the same type. The end-user, or you as a developer, can easily forget which one needs to come first and can mess them up. Due to the same type, the program will not fail, and you will get a logic error:

shipping_charge(2000, True, False)

or

shipping_charge(2000, False, True)

You can add keyword arguments with default values. This is good practice:

def shipping_charge(weight, pnds=False, exceed=False):
    if pnds:
       weight /= 2.2
    if exceed and weight > 1000:
       raise Exception("Weight can't exceed 1000 kg")
    if weight < 0:
       raise ValueError("Can't calculate shipping charge of negative weights")
    for min_weight, rate in WEIGHT_RATES:
        if weight > min_weight:
            return weight * rate

But, the problem is not solved. To resolve this issue, you need to add one star in the argument list:

def shipping_charge(weight, *, pnds=False, exceed=False):
    if pnds:
       weight /= 2.2
    if exceed and weight > 1000:
       raise Exception("Weight can't exceed 1000 kg")
    if weight < 0:
       raise ValueError("Can't calculate shipping charge of negative weights")
    for min_weight, rate in WEIGHT_RATES:
        if weight > min_weight:
            return weight * rate

That’s it, the next time you call this function, you will get an error:

>>>shipping_charge(2000, True, False)
TypeError: shipping_charge() takes 1 positional argument but 3 were given

More info: PEP-3102.


Thank you for reading!

Any questions? Leave your comment below to start fantastic discussions!

Check out my blog or come to say hi on Twitter or subscribe to my telegram channel.

Plan your best!

Further Reading





Source link

Write A Comment