A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://realpython.com/what-does-isinstance-do-in-python/ below:

What Does isinstance() Do in Python? – Real Python

Python’s isinstance() function helps you determine if an object is an instance of a specified class or its superclass, aiding in writing cleaner and more robust code. You use it to confirm that function parameters are of the expected types, allowing you to handle type-related issues preemptively. This tutorial explores how isinstance() works, its use with subclasses, and how it differs from type().

By the end of this tutorial, you’ll understand that:

Exploring isinstance() will deepen your understanding of the objects you work with and help you write more robust, error-free code.

To get the most out of this tutorial, it’s recommended that you have a basic understanding of object-oriented programming. More specifically, you should understand the concepts of classes, objects—also known as instances—and inheritance.

For this tutorial, you’ll mostly use the Python REPL and some Python files. You won’t need to install any libraries since everything you’ll need is part of core Python. All the code examples are provided in the downloadable materials, and you can access these by clicking the link below:

Get Your Code: Click here to download the free sample code that you’ll use to learn about isinstance() in Python.

Take the Quiz: Test your knowledge with our interactive “What Does isinstance() Do in Python?” quiz. You’ll receive a score upon completion to help you track your learning progress:

Interactive Quiz

What Does isinstance() Do in Python?

Take this quiz to learn how Python's isinstance() introspection function reveals object classes and why it might not always show what you expect.

It’s time to start this learning journey, where you’ll discover the nature of the objects you use in your code.

Why Would You Use the Python isinstance() Function?

The isinstance() function determines whether an object is an instance of a class. It also detects whether the object is an instance of a superclass. To use isinstance(), you pass it two arguments:

  1. The instance you want to analyze
  2. The class you want to compare the instance against

These arguments must only be passed by position, not by keyword.

If the object you pass as the first argument is an instance of the class you pass as the second argument, then isinstance() returns True. Otherwise, it returns False.

Note: You’ll commonly see the terms object and instance used interchangeably. This is perfectly correct, but remembering that an object is an instance of a class can help you see the relationship between the two more clearly.

When you first start learning Python, you’re told that objects are everywhere. Does this mean that every integer, string, list, or function you come across is an object? Yes, it does! In the code below, you’ll analyze some basic data types:

You create two variables, shape and number, which hold str and int objects, respectively. You then pass shape and str to the first call of isinstance() to prove this. The isinstance() function returns True, showing that "sphere" is indeed a string.

Next, you pass number and int to the second call to isinstance(), which also returns True. This tells you 8 is an integer. The third call returns False because 8 isn’t a floating-point number.

Knowing the type of data you’re passing to a function is essential to prevent problems caused by invalid types. While it’s better to avoid passing incorrect data in the first place, using isinstance() gives you a way to avert any undesirable consequences.

Take a look at the code below:

Your function takes two numeric values, multiplies them, and returns the answer. Your function works, but only if you pass it two numbers. If you pass it a number and a string, your code won’t crash, but it won’t do what you expect either.

The string gets replicated when you pass a string and an integer to the multiplication operator (*). In this case, the "3" gets replicated five times to form "33333", which probably isn’t the result you expected.

Things get worse when you pass in two strings:

The multiplication operator can’t cope with two strings, so the code crashes. This is where you could use isinstance() to warn the user about the invalid data.

The improved version of your calculate_area() function demonstrates this:

To avoid unexpected results, you use two calls to isinstance(), along with the Boolean and operator, to check that you haven’t passed either length or breadth as a string—or any other non-integer—by mistake.

If isinstance() detects invalid data, the if statement causes your function to raise a TypeError exception. Of course, if two integers are passed, the results will be the same as before.

In practice, you should also check whether the length and breadth parameters could be float types. You’ll learn how to incorporate multiple checks into isinstance() later.

This example of checking for a data type illustrates a common usage of isinstance(). You’ve reduced the chance of invalid results wandering further through your code.

Now that you’ve been introduced to the basics of isinstance(), you’ll move on to learn how it can be used to analyze instances within a class hierarchy.

Can isinstance() Detect Subclasses?

In addition to detecting the class of an instance, isinstance() can also tell you if your instance is an object of a superclass. Remember that an instance is considered an object of its parent class, any of its superclasses, and the class you used to create it.

Suppose you’re creating a class hierarchy for a pool game simulator. You could begin with a Ball class containing .color and .shape data attributes, along with .rebound() and .detect_collision() methods. You could then create a PoolBall subclass for your game. It would inherit everything Ball has, but you could also tweak its contents to meet the specific requirements of the pool game.

Having done this, you might decide to design some more ball game simulations. Instead of creating a fresh Ball class, you could reuse your existing one and create more subclasses.

For example, Ball could serve as the superclass for the SoccerBall, PoolBall, and AmericanFootBall classes, each sharing the same basic content but implementing methods differently to behave appropriately within their respective games.

Take the .rebound() method defined within the AmericanFootBall class, whose shape resembles a prolate spheroid. This method will require different calculations from those of SoccerBall and PoolBall instances, which are both spheres.

Note: In this tutorial, football refers to American football, while soccer refers to association football.

The code below defines some Ball subclasses:

This code defines a straightforward class hierarchy containing a Ball superclass with two subclasses named PoolBall and AmericanFootBall. When you create an instance of the Ball class, you must pass it values for its .color and .shape attributes.

The data you pass when you create your instance is defined within the .__init__() instance initializer method. This gets called automatically each time you create an instance of the class. You can use it to make any colored ball and give it any shape you wish.

Now, take a closer look at the PoolBall class. To create this as a subclass of Ball, you define it using class PoolBall(Ball). Your PoolBall will have access to all the methods and data attributes from Ball.

When you create a PoolBall instance, you pass it a color and number, but not a shape, since this is all .__init__() demands. However, PoolBall instances still have a .shape data attribute that needs to be initialized.

To initialize .shape, you call the original .__init__() method defined in the Ball superclass using super().__init__(color, shape="sphere"). This passes your desired color and the string "sphere" to the superclass initializer. The string "sphere" is assigned to the shape parameter in the parent class. All PoolBall instances will always be spherical in shape with the color you desire.

To ensure that the number value is assigned to the .number attribute, you again use self.number. Your new PoolBall instance will have a color, a shape of "sphere", and a number.

Similarly, any AmericanFootBall instances will have a .color attribute and "prolate spheroid" as their .shape attribute value. They won’t have a .number attribute because it isn’t needed.

Investigating How isinstance() Treats Subclasses

Using the class hierarchy you’ve just developed, you need to create some instances for isinstance() to analyze:

To create instances, you pass the required values to each class. So, PoolBall("black", 8) will create a new PoolBall instance, which will be black in color, with a spherical shape and a number eight. Similarly, you create a brown, prolate spheroidal American football and a more general green spherical ball.

Next, you’ll investigate these classes and instances with isinstance(), starting with eight_ball:

Look carefully at the first two isinstance() calls. Unsurprisingly, eight_ball has been detected as both a PoolBall and a Ball. This second result is True because any instance of a subclass is also an instance of its superclasses.

Now look at the third call. It returns False because eight_ball, being a PoolBall, isn’t an AmericanFootBall. In human terms, they share the same parent, so they’re more like siblings.

You’ve just seen that any instance of a subclass is also an instance of its superclass. This could be considered a special case. The wider rule is that any instance of a subclass is also an instance of all its superclasses. This means that an instance belongs not only to the class it was created from but also to its parent class, grandparent class, and so on, all the way up the hierarchy.

Nothing lasts forever, and Python’s inheritance tree is no different. The lineage needs to stop somewhere. That somewhere is the object superclass, which sits at the top of the hierarchy and is the class from which all other classes are derived.

Take a look at this code:

As you can see, everything is an object instance—even object itself.

Earlier, you saw how int, float, and str are classes you commonly use when working with basic data types. Another type is bool, which can hold only True or False. Incidentally, this is also the data type returned by isinstance().

Like all other types in Python, bool is a class. However, it’s also a subclass of int. This means that, in addition to being instances of bool, both True and False are also instances of int:

As you can see, True and False are instances of both int and bool types. However, while the code int(True) will return 1 and int(False) will return 0, the integers 1 and 0 and the Booleans True and False are different. While a bool type is an integer, an int type isn’t a Boolean.

Unless you’re careful, this can cause problems when you need to check for non-Boolean integers in situations where bool values may also be present:

As you can see, everything is being flagged as an int. One solution could be this:

This time, the results are accurate. An alternative approach would be to use type(). As its name suggests, this will tell you the data type of the data passed to it:

While using type() works in this case, its purpose isn’t the same as isinstance(). You’ll learn more about this later.

Note: Python classes each have an .mro() class method. This tells you the method resolution order (MRO), which is the order that Python follows to locate data attributes and methods in a class hierarchy. It’s particularly relevant when you’re dealing with multiple inheritance.

Although it’s not directly related to using isinstance(), you can use .mro() to inspect the class hierarchy your class belongs to.

Take a look at the class hierarchy shown below:

The Top class has a subclass named Middle, which has a subclass named Bottom. When you call the .mro() method on Bottom and read the output from left to right, you can see the relationship between Bottom and its various superclasses. Note that Top isn’t the end of the hierarchy—the object class is.

Before you go any further, it’s time to consolidate your learning.

Consolidating Your Learning

To check your understanding of what you’ve learned so far, see if you can answer the following questions:

Answer the following questions based on the Ball hierarchy used above:

  1. Is football an instance of AmericanFootBall?
  2. Is football an instance of PoolBall?
  3. Is ball an instance of Ball?
  4. Is ball an instance of AmericanFootBall?
  5. Is football an instance of Ball?
  6. Is ball an instance of PoolBall?
  7. Is the integer 1 an instance of bool?
  8. Is the integer 0 an instance bool?

You can confirm your answers to each question using isinstance():

Did you get them all correct? Well done if you did!

Now that you’ve had some experience with isinstance(), next you’ll learn why it’s often better to use isinstance() instead of type() to determine an object’s class.

How Does isinstance() Differ From type()?

The isinstance() function is just one example of several introspection functions that allow you to examine Python objects to learn more about them. As you just learned, Python also provides type(). If you pass it an instance, then you’ll be rewarded with the class to which that instance belongs.

Consider once more the Ball and PoolBall classes you created earlier. To begin with, you create a new PoolBall instance:

You can see from the code that eight_ball is an instance of PoolBall. You can also use type() to confirm this:

As expected, type() returns details of the class to which eight_ball belongs—in this case, a PoolBall class.

However, you should be careful when using it because it isn’t designed to identify superclass membership. For example, because eight_ball is a PoolBall, and PoolBall is a subclass of Ball, isinstance() will confirm that eight_ball is also a Ball, but type() won’t:

While isinstance() indeed confirms what you know to be true, at first glance, type() appears to disagree.

The reason type() returns False is because it’s not designed to recognize inheritance hierarchies. Unlike isinstance(), type() is designed to look at an instance and tell you the class that instance was created from. When you attempt to use type() to interrogate any further up the class hierarchy, you’re using the wrong tool for the job.

Earlier, you used type() to check for the bool type. This is perfectly safe because the bool class has been designed so that it can’t be subclassed. In other words, there will never be any subclasses of bool that could be passed to type(). So, using type() to check whether a bool is a subclass of int would be pointless.

Note: In earlier versions of Python, type() was also considered slower than isinstance(). In current versions, though, the difference is negligible. Depending on what you pass to each function, type() sometimes even slightly outperforms isinstance(). If you’re in a situation where either could be used, then it might be a good idea to time test both to see which is faster.

Next, you’ll learn how to extend the basic functionality of isinstance().

Can You Use isinstance() to Check for Multiple Types?

Besides being able to tell you if your instance belongs to a single class, you can also use isinstance() to determine if it belongs to one of several classes. To do this, pass in a tuple of classes. There’s no need to make separate isinstance() calls.

Suppose you wanted to determine whether data was an integer or a floating-point number. Here’s one way you could do it:

By using this code, you’re checking whether a value, first 3.14, then "3.14", is either an integer or a floating-point number. To do this, you pass in a tuple containing both class types you want to check for as the second parameter of isinstance(). In the first case, 3.14, because it’s a float, is shown to be a number. In the second case, "3.14" is a string, so it’s not considered a number.

You can even pass a nested tuple, meaning a tuple that contains other tuples, as the second argument to isinstance():

In using a nested tuple, you’ve created a semantically equivalent piece of code to your earlier example. The first example contains a nested tuple, while the second contains a flat tuple, as before. The trailing commas surrounding int and float in the first example are necessary to ensure tuples are present because each has only one element. Try replacing 3.14 with "3.14" and you’ll see the same Not a number result as before.

Using nested tuples is helpful if the nested tuple containing the types to be checked is constructed using existing tuples from different sources. In most cases, you’ll rarely use nested tuples in this way.

Although passing multiple types to isinstance() is common, you can also use a union type expression. This allows you to group together multiple data types separated by the bitwise OR (|) operator. When you pass a type to be tested into isinstance(), the function will return True if the type matches any of the types defined in your union.

Note: Union types are actually designed for type checking to improve code readability. They also work with isinstance().

Instead of passing the tuple (int, float) to isinstance(), you could do the following:

In both examples, you’ve replaced the earlier (int, float) tuple with the int | float union type expression. Unsurprisingly, the results are the same.

Note: The only types that isinstance() supports are single class types, tuples of class types, nested tuples of class types, and union type expressions. If you try using a list, dictionary or anything else, isinstance() will fail. Other introspection functions, such as issubclass(), also only accept tuples, so there’s consistency.

Guido van Rossum made a design choice to allow only tuples for a few reasons:

Next, you’ll see how isinstance() works with abstract base classes.

How Can You Use isinstance() With Abstract Base Classes?

Earlier, you learned how isinstance() can tell you if an object is an instance of a class or one of its superclasses. You also know that creating subclasses helps avoid reinventing the wheel when something similar to one of your existing classes becomes necessary. In some cases, you’ll never need to use instances of these superclasses. This is where you might find abstract base classes useful.

Creating an Abstract Base Class

Thinking back to your Ball example, every ball has a specific use. When you play with a ball in everyday life, you’re really playing with a soccer ball, a pool ball, and so on. Since these real-world balls are not direct instances of Ball but only instances of its subclasses, the Ball class is a good candidate for consideration as an abstract base class. Conversely, subclasses designed to be implemented are called concrete classes.

Before you can see how isinstance() deals with subclasses, you’ll redesign your earlier hierarchy to make the Ball class abstract:

Here, you use the appropriately named abc module to help you create abstract base classes. To make your Ball class an abstract class, your Ball class must inherit from the ABC class, which you import from the abc module in Python’s standard library. Therefore, you can make Ball an abstract base class using the notation Ball(ABC) when you define the class.

One of the most prevalent features of abstract classes is abstract methods. Abstract methods act as placeholders for methods that their subclasses will require. They typically don’t contain any implementation details because the implementation is specific to each subclass.

When you create an instantiable subclass of an abstract base class, you usually define an implementation for each abstract method in the subclass. If you don’t, then you’ll raise a TypeError when you try to create instances of these subclasses because they’ll still be abstract.

To designate a method as abstract, you must decorate it with the @abstractmethod decorator. This decorator is also provided for you courtesy of abc.

In your balls_v2.py file, you first define a new abstract version of your Ball class. As with the earlier version, its .__init__() method sets up the initial values of its .color and .shape data attributes exactly as .__init__() did in the original version.

You also include a new abstract method named .get_state(). The implementation of this method must appear in any subclasses of Ball. However, in its abstract form, you use the pass statement instead of an implementation. This appeases the IndentationError exception you’d see if you tried to create a method without any code in its body.

You also redefine your earlier PoolBall and AmericanFootBall classes. This time, you’re forced to provide an implementation of .get_state() in each. Remember that without this implementation, you wouldn’t be able to create instances of them.

As a quick check to see if the abstraction is working, you try to create a new Ball instance:

As you can see, your attempt to instantiate Ball has failed miserably. However, Ball is still useful to isinstance(), as you’ll see next.

Learning How isinstance() Treats Abstract Base Classes

Although you can no longer create instances of Ball, there’s nothing to stop you from instantiating the other two classes:

After importing both classes, you manage to instantiate objects from each successfully. You can then use these new objects to see how isinstance() interprets abstract classes:

Although you’ve added abstraction into your infrastructure, isinstance() treats abstract base classes like any other superclass. Both cases show that PoolBall and AmericanFootBall are considered instances of their common abstract superclass. This probably isn’t that surprising—an abstract class is just another class in the hierarchy, after all.

Does isinstance() Use Duck Typing?

One interesting aspect of using isinstance() is that it sometimes uses duck typing to decide whether your instance belongs to a class. When you use duck typing, class membership isn’t decided by its true membership, but by its abilities.

Suppose an instance of one of your classes shares similar behavior to another unrelated class. In that case, using duck typing means you’d consider it an instance of that unrelated class. In other words:

If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck. (Source)

However, duck typing can cause isinstance() to produce surprising results, as you’ll see next.

Introducing collections.abc

To see some examples of duck typing, you’ll use the Iterable abstract base class from the collections.abc library, because isinstance() uses duck typing with many classes from this library.

In Python, an iterable is an object that allows you to iterate over it. In other words, you can work linearly through your iterable and retrieve each of its elements. You use iterables whenever you use common collections such as lists and tuples. It’s because both lists and tuples are iterables that you can use in Python for loops.

Note: To learn more about iterables, check out Iterators and Iterables in Python: Run Efficient Iterations.

Suppose you decide to write your own iterable to store various pool players. One way of doing this is to subclass the Iterable abstract base class from the collections.abc module:

To create a PlayersVersionOne instance, you pass in a Python iterable, such as a list or tuple of pool players. This becomes its .players data attribute.

The Iterable abstract base class requires you to implement .__iter__() in your subclasses. This method is designed to return an iterator object to manage element retrieval. You use the built-in iter() function within .__iter__() to return the iterator associated with .players. It’s the presence of .__iter__() that makes your PlayersVersionOne an iterable and allows it to work within a for loop:

First, you import your new PlayersVersionOne class and create a new instance that contains a list of pool players. By using this instance in a for loop, you can iterate over it and print each value. Your PlayersVersionOne instance behaves like an iterable.

You can use isinstance() to confirm this:

You’re probably not surprised to see that your PlayersVersionOne instance is reported as an instance of Iterable. After all, PlayersVersionOne is a subclass of collections.abc.Iterable. However, things aren’t quite as simple as they seem at first, as you’ll see in the next section.

Understanding How isinstance() Uses Duck Typing

What may surprise you is that isinstance() actually uses duck typing. When you use isinstance() to test for Iterable, it returns True because the iteration is done using .__iter__(). There’s actually no need to subclass Iterable. Any class with an .__iter__() method is considered an Iterable, regardless of what that method does.

Consider the PlayersVersionTwo class that you add to your player_iterables.py file:

The functionality of your PlayersVersionTwo class is identical to that of your PlayersVersionOne class, only this time, PlayersVersionTwo isn’t a subclass of Iterable.

Suppose you run the same analysis code against this version:

As you can see, the output is identical. The isinstance() function still considers PlayersVersionTwo an instance of Iterable, not because it is, but because it implements .__iter__(). Duck typing is present.

Next, you create a class that’s still capable of being used in for loops but won’t be detected by isinstance() as an instance of Iterable. Consider the PlayersVersionThree version of your iterable:

This time, there’s no .__iter__() method in sight. This code uses .__getitem__() to retrieve each element in the .players data attribute and return it.

Note: Although .__getitem__() is used here for iteration, this is an older style of iteration in Python.

The main role of the this special method is to allow you to access instances of the class in which it’s defined using the square brackets notation ([]), similar to how you access elements of a Python list.

You should use .__iter__() to make an instance of a class iterable in modern Python since this uses Python’s iterator protocol.

You analyze PlayersVersionThree in the same way as before:

You can see that while instances of PlayersVersionThree are most certainly iterables because they work in for loops, isinstance() reveals they’re not instances of Iterable because it’s looking for .__iter__() .

Indeed, isinstance() will give you the same True value for any instance you pass it that implements .__iter__(), regardless of what gets returned:

This time, although PlayersVersionFour contains .__iter__(), the method uses pass to make it do nothing. However, isinstance() still thinks PlayersVersionFour is an Iterable:

It’s impossible to use instances of PlayersVersionFour in a for loop because it’s not iterable. You might want to try this yourself.

Note: Clearly, you can’t rely on isinstance() to detect iterables. The best method is to use the iter() function you saw earlier. If iter() returns a reference to an iterator object, then an iterable is present. If it returns an error, then it’s not:

Only the first three custom objects you defined are iterable and can be looped over.

You might wonder when isinstance() decides to use duck typing instead of a class hierarchy search. It only uses duck typing if the class it’s testing contains a special .__instancecheck__() method. If present, .__instancecheck__() defines what methods the instance must contain. If this method is absent, then isinstance() will check the class hierarchy to which the instance belongs.

In the case of the Iterable class, its .__instancecheck__() method instructs isinstance() to ensure the .__iter__() method is present. If it exists, then the instance is deemed an Iterable.

Many of the classes within collections.abc instruct isinstance() to work in this way. So, just because an instance isn’t an Iterable, it doesn’t mean it can’t be iterated over. The collections.abc documentation will tell you what methods isinstance() looks for.

This is probably a good time to consolidate what you’ve just learned.

Consolidating Your Learning

To wrap up your learning about isinstance() and how to use it with abstract base classes, why not try answering the following questions?

  1. Earlier, you created an abstract Ball class with two concrete classes, PoolBall and AmericanFootBall. You then proved that instances of both classes are instances of Ball. Is there any other class mentioned in the code that isinstance() would also recognize both eight_ball and football as instances of?

  2. The collections.abc.Callable is another essential abstract base class. See if you can find out what this abstract class represents. Can you think of any examples of Callable instances you’ve used in this tutorial?

  3. Is there anything inside the PoolBall and AmericanFootball classes that’s also Callable?

  1. If you look at the code closely, then you’ll see that the Ball class is a subclass of ABC. Remember that isinstance() can recognize class hierarchies:

    Instances of PoolBall and AmericanFootball are both instances of ABC.

  2. All functions are callable, as are classes. Any of the functions or classes mentioned in this tutorial are instances of Callable:

    As you can see, the isinstance() function and PoolBall are Callable instances.

  3. Methods are also instances of the Callable class:

    As you can see, the .get_state() method of a PoolBall instance—or of any other subclass of Ball—is indeed Callable.

    Note that you passed eight_ball.get_state into isinstance(), not eight_ball.get_state(). If you had done the latter, you would have called .get_state(), which would return the string "sphere". Since that’s just a string, "sphere" isn’t an instance of Callable.

By getting to this stage, you’ve had a good workout using isinstance(). However, your learning journey doesn’t have to end here.

What Should You Learn Next?

Congratulations on reaching the end of this tutorial! Hopefully, it’s sparked your interest in using isinstance() to perform introspection on your objects. This can help you better understand the objects your program uses and the relationships between them.

However, isinstance() isn’t the only introspection function you can use. You might like to delve deeper and look into the following commonly used functions:

These aren’t the only built-in functions or introspection tools available. You can find more in Python’s inspect module.

Conclusion

This tutorial showed you how to use isinstance() to investigate the type of an object. You should now have a solid understanding of how to use it, along with some of its gotchas.

In this tutorial, you’ve learned how to:

While you’ve had a solid overview of what isinstance() can do, you’re strongly encouraged to practice what you’ve learned and to explore introspection further. Understanding what these functions reveal will help you better grasp how your code works and the objects you’re working with.

Get Your Code: Click here to download the free sample code that you’ll use to learn about isinstance() in Python.

Frequently Asked Questions

Now that you have some experience with isinstance() in Python, you can use the questions and answers below to check your understanding and recap what you’ve learned.

These FAQs are related to the most important concepts you’ve covered in this tutorial. Click the Show/Hide toggle beside each question to reveal the answer.

You use isinstance() to determine if an object is an instance of a specified class or its superclass.

You can check if an object is a member of a class by using the isinstance() function, passing the object and the class as arguments.

The isinstance() function checks if an object is an instance of a class or any of its superclasses, while type() only returns the object’s exact class without considering inheritance.

Yes, isinstance() works with subclasses by returning True if the object is an instance of the specified class or any of its superclasses.

Take the Quiz: Test your knowledge with our interactive “What Does isinstance() Do in Python?” quiz. You’ll receive a score upon completion to help you track your learning progress:

Interactive Quiz

What Does isinstance() Do in Python?

Take this quiz to learn how Python's isinstance() introspection function reveals object classes and why it might not always show what you expect.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4