Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python

Michael Strasser
Michael Strasser
3,073 Points

Object Oriented Python - typed parameters?

Hey, I was wondering if there is a good way to check if a variable is an instance of a certain class, preferably in the parameter part of a function. I'm thinking about building an application which has a class that takes an undefined number of parameters, but these parameters have to be of a certain type. Now I know I could just write a function check_type or something like that which is called in the class constructor. If I write this in a generic way, I could call it in the constructor of every class that needs this functionality. But this seems to me like some kind of "hack", because it feels like Python does not want me to do that ;)

So my question is basically: If you go about building a python application, I feel like this should be a situation which you encounter more often than you like, so there has to be some kind of best practice for this. Am I missing some kind of python functionality, is my approach which I described above the usual way to do this or do you just don't care about this stuff and it's the responsibility of the other code parts to just call your function correctly?

Best regards, Michael

3 Answers

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 68,423 Points

There isn't a way to check arguments against desired parameter types within the parameter statement, but you can use a decorator to add argument checking as a wrapper around any function.

From this StackOverflow answer

From the Decorators for Functions and Methods PEP-318:

def accepts(*types):
    def check_accepts(f):
        assert len(types) == f.func_code.co_argcount
        def new_f(*args, **kwds):
            for (a, t) in zip(args, types):
                assert isinstance(a, t), \
                       "arg %r does not match %s" % (a,t)
            return f(*args, **kwds)
        new_f.func_name = f.func_name
        return new_f
    return check_accepts

Usage:

@accepts(int, (int,float))
def func(arg1, arg2):
    return arg1 * arg2

func(3, 2) # -> 6
func('3', 2) # -> AssertionError: arg '3' does not match <type 'int'>

Mocking up some code to create a two decorators. The first decorator_type_check_kwargs follows your code example to check the type of the keyword arguments against a provided type. It seems that using kwargs is overkill, so I created a second decorator to work with position args call decorator_type_check_args. Below is the code snippet followed by the output in the REPL:

def decorator_type_check_kwargs(darg1):
    """decorate a function with an argument - check kwargs against type
    """
    def wrap(func):
        def dunder_init(*args, **kwargs):
            # print args (removing instance reference)
            print("{} arguments were positional:{}, keyword:{}"
                  "".format(func.__name__, args[1:], kwargs))
            print("Checking for class type {}".format(darg1))  # .__class__))
            for arg in kwargs.values():
                if isinstance(arg, darg1):
                    print("arg {} is OK. It's of type {}".format(arg, darg1))
                else:
                    print("arg {} is BAD! It's type {} is NOT of type {}"
                          "".format(arg, type(arg), darg1))  # , type(darg1)))
            return func(*args, **kwargs)
        return dunder_init
    return wrap

def decorator_type_check_args(darg1):
    """decorate a function with an argument - check args against type
    """
    def wrap(func):
        def dunder_init(*args, **kwargs):
            # print args (removing instance reference)
            print("{} arguments were positional:{}, keyword:{}"
                  "".format(func.__name__, args[1:], kwargs))
            print("Checking for class type {}".format(darg1))  # .__class__))
            for arg in args[1:]:
                if isinstance(arg, darg1):
                    print("arg {} is OK. It's of type {}".format(arg, darg1))
                else:
                    print("arg {} is BAD! It's type {} is NOT of type {}"
                          "".format(arg, type(arg), darg1))  # , type(darg1)))
            return func(*args, **kwargs)
        return dunder_init
    return wrap


class SecondClass():
    """General class does nothing special"""
    myclasses = []

    @decorator_type_check_kwargs(int)
    def __init__(self, **kwargs):
        """Class is initialized with variable number of objects all of
        the same type. These are added to the 'myclasses' list
        """
        for value in kwargs.items():
            self.myclasses.append(value)

class ThirdClass():
    """General class does nothing special"""
    myclasses = []

    @decorator_type_check_args(str)
    def __init__(self, *args):
        """Class is initialized with variable number of objects all of
        the same type. These are added to the 'myclasses' list
        """
        for value in args:
            self.myclasses.append(value)


print("\n\ninstantiate SecondClass")
second_class = SecondClass(arg1="string1", arg2=25)
print("\nCheck second class myclasses {}".format(second_class.myclasses))

print("\n\ninstantiate ThirdClass")
third_class = ThirdClass("boat", 42)
print("\nCheck third class myclasses {}".format(third_class.myclasses))

REPL output when executing snippet

Python 3.4.3 (default, Oct 14 2015, 20:28:29) 
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> >>> >>> >>> 

instantiate SecondClass
__init__ arguments were positional:(), keyword:{'arg2': 25, 'arg1': 'string1'}
Checking for class type <class 'int'>
arg 25 is OK. It's of type <class 'int'>
arg string1 is BAD! It's type <class 'str'> is NOT of type <class 'int'>

Check second class myclasses [('arg2', 25), ('arg1', 'string1')]


instantiate ThirdClass
__init__ arguments were positional:('boat', 42), keyword:{}
Checking for class type <class 'str'>
arg boat is OK. It's of type <class 'str'>
arg 42 is BAD! It's type <class 'int'> is NOT of type <class 'str'>

Check third class myclasses ['boat', 42]
>>> 

This is just a jumping of point. Instead of the print statements you could raise errors as needed.

Chris Freeman
Chris Freeman
Treehouse Moderator 68,423 Points

You think my answer was best? Check it now! it's Super-Best! I added a working mock up of a decorator in the style you might be looking for. It is only a template but should get you started. I left a lot of print statements so you can see how it works. Good Luck!

Michael Strasser
Michael Strasser
3,073 Points

Thanks a lot, this is very helpful :)

Ken Alger
STAFF
Ken Alger
Treehouse Teacher

Michael;

To make sure I am understanding, you want to be able to have something along the lines of:

a = "Hello World"

and then be able to determine if a is an instance of the string class, for example? If that is the case, Python has a built in class isinstance that would let you do something like:

a = "Hello"

if isinstance(a, str):
    print(a + " is of type string")
else:
    print("Nope")

Does that help? Post back with further questions.

Happy coding,
Ken

Michael Strasser
Michael Strasser
3,073 Points

Hey Ken, thanks for the reply. Here's the situation that I mean: I have some sort of python class, which I wrote:

class Myclass:
   a = "Hello"

Now I have another class with a constructor that takes an arbitrary amount of variables:

class Sndclass:
     myclasses = []

    def __init__(**kwargs):
        for value in kwargs.items():
            myclasses.append(value);

I am aware that I could call the isInstance function in the for loop, but coming from a statically typed background I was wondering if there is a good way to tell Python to only accept variables of type "Myclass" as parameters for the constructor of Sndclass, or which way would be the best to solve this situation.

Ken Alger
Ken Alger
Treehouse Teacher

Hmmm... not sure how to limit parameters like that. Perhaps Chris Freeman would have some insights.

Chris Freeman
Chris Freeman
Treehouse Moderator 68,423 Points

Verifying argument types needs to be done in the __init__ method. My posted answer can be extended to create a decorator for use on the __init__ method.

Otherwise you'll have explicitly loop through the parameters checking for isinstance() or create a function to do this so that your code stays DRY.

Another alternative is to create a mix in class that can be used in the declaration of SndClass or in the other class needing this specific parameter filtering.

Chris Freeman
Chris Freeman
Treehouse Moderator 68,423 Points

Ken, BTW, I never got an email notice that I was tagged in this thread. I normally don't check the "Ringing Bell" icon for notices. I noticed this thread only because I was perusing the posts :-/