Class object can only call a method if object attribute is between a range

Viewed 59

I am looking to make my code more sophisticated and this is the issue that I am running into

class Person:

    def __init__(self, age):
        self.age = age

    def drives(self):
        if self.age >= 18:
            # Do more things which driving entitles
            print("you can drive")

    def studies(self):
        if self.age <= 25:
            # Do more student stuffs
            print("Good luck with your education")

bob = Person(14)
bob.drives()
bob.studies()

jim = Person(35)
jim.drives()
jim.studies()

I don't like the checks as soon as I enter the method, increasing markup. I know about decorators, are they the best thing to do here? And how would I use them for this use case? What I would like it to look like is:

class Person:

    def __init__(self, age):
        self.age = age

    @check_if_person_age_is_greater_than_17
    def drives(self):
        # Do more things which driving entitles
        print("you can drive")

    @check_if_person_age_is_less_than_26
    def studies(self):
        # Do more student stuffs
        print("Good luck with your education")

bob = Person(14)
bob.drives() # either this method cannot be accessed or returns nothing, since bob is 14
bob.studies() # this should work normally

jim = Person(35)
jim.drives() # this should work normally
jim.studies() # either this method cannot be accessed or returns nothing, since jim is 35

I am sorry if my question isn't concise or worthwhile.

1 Answers

So this is what I was able to come up with. This cleans up the code quite a lot but may make it a little difficult to understand. You can also make the decorator into a classmethod or staticmethod, if that is your requirement.

from functools import wraps


def prerequisite(age_limit=18, _func=None):
  def _wrapper_function(func):
    @wraps(func)
    def _inner_function(*args, **kwargs):
        if args[0].age >= age_limit:
          return func(*args, **kwargs)
        else:
          raise ValueError("You can't {func_name} at the age of {age}".format(func_name=func.__name__, age=args[0].age))
    return _inner_function
  if _func is None:
      return _wrapper_function
  else:
      return _wrapper_function(_func)

class Person(object):
    def __init__(self, age):
      self.age = age

    @prerequisite(age_limit=16)
    def drive(self):
      # Do more drive stuffs
      print("You are eligible to apply for a license to drive. Please drive responsibly.")

    @prerequisite(age_limit=21)
    def drink(self, drink_name):
      # Do more drink stuffs
      # Passing an argument just to show that arguments can also be passed like this.
      print("You can legally drink. Alcohol is dangerous to health, drink cautiously. Your favorite drink is {drink_name}".format(drink_name=drink_name))

    @prerequisite()
    def fly(self):
      # Do more fly stuffs
      # This is an example for how default args for decorator works
      print("You are eligible to apply for a license to fly.")

bob = Person(19)

bob.drive() # works
bob.fly() # works
bob.drink("Beer") # This will throw an exception
Related