ACHIEVEMENTS

We help our clients integrate, analyze, and use their data to improve their business.

36

GREAT POST

300

HAPPY CLIENTS

650

COFFEES DRUNK

2000

PAGE VIEW

  • Using Classes In Python - Part 5




    Using the Class in an Application



    Most of the time, you use external classes when working with Python. It isn’t very often that a class exists within the confines of the application file because the application would become large and unmanageable. In addition, reusing the class code in another application would be difficult. The following steps help you use the MyClass class that you created in the previous section.

     Open a Python File window.

    You see an editor in which you can type the example code.

     Type the following code into the window — pressing Enter after each line:

    import MyClass

    SamsRecord = MyClass.MyClass()
    AmysRecord = MyClass.MyClass("Amy", 44)

    print(SamsRecord.GetAge())
    SamsRecord.SetAge(33)

    print(AmysRecord.GetName())
    AmysRecord.SetName("Aimee")

    print(SamsRecord)
    print(AmysRecord)

    The example code begins by importing the MyClass module. The module name is the name of the file used to store the external code, not the name of the class. A single module can contain multiple classes, so always think of the module as being the actual file that is used to hold one or more classes that you need to use with your application.

    After the module is imported, the application creates two MyClass objects. Notice that you use the module name first, followed by the class name. The first object, SamsRecord, uses the default settings. The second object, AmysRecord, relies on custom settings.

    Sam has become a year old. After the application verifies that the age does need to be updated, it updates Sam’s age.

    Somehow, HR spelled Aimee’s name wrong. It turns out that Amy is an incorrect spelling. Again, after the application verifies that the name is wrong, it makes a correction to AmysRecord. The final step is to print both records in their entirety.

     Choose RunRun Module.

    The application displays a series of messages as it puts MyClass through its paces. At this point, you know all the essentials of creating great classes.


    Extending Classes to Make New Classes


    As you might imagine, creating a fully functional, production-grade class (one that is used in a real-world application actually running on a system that is accessed by users) is time consuming because real classes perform a lot of tasks. Fortunately, Python supports a feature called inheritance. By using inheritance, you can obtain the features you want from a parent class when creating a child class. Overriding the features that you don’t need and adding new features lets you create new classes relatively fast and with a lot less effort on your part. In addition, because the parent code is already tested, you don’t have to put quite as much effort into ensuring that your new class works as expected. The following sections show how to build and use classes that inherit from each other.


     

    Building the child class


    Parent classes are normally supersets of something. For example, you might create a parent class named Car and then create child classes of various car types around it. In this case, you build a parent class named Animal and use it to define a child class named Chicken. Of course, you can easily add other child classes after you have Animal in place, such as a Gorilla class. However, for this example, you build just the one parent and one child class, as shown in Listing 14-2.

    Listing 14-2:  Building a Parent and Child Class

    class Animal:
    def __init__(self, Name="", Age=0, Type=""): self.Name = Name
    self.Age = Age self.Type = Type

    def GetName(self): return self.Name

    def SetName(self, Name): self.Name = Name

    def GetAge(self): return self.Age

    def SetAge(self, Age): self.Age = Age

    def GetType(self): return self.Type

    def SetType(self, Type): self.Type = Type

    def __str__(self):

    return "{0} is a {1} aged {2}".format(self.Name, self.Type, self.Age)


    class Chicken(Animal):

    def __init__(self, Name="", Age=0): self.Name = Name
    self.Age = Age self.Type = "Chicken"

    def SetType(self, Type):
    print("Sorry, {0} will always be a {1}"
    .format(self.Name, self.Type))

    def MakeSound(self):
    print("{0} says Cluck, Cluck, Cluck!".format(self.Name))


    The Animal class tracks three characteristics: Name, Age, and Type. A production application would probably track more characteristics, but these characteristics do everything needed for this example. The code also includes the required accessors for each of the characteristics. The __str__() method completes the picture by printing a simple message stating the animal characteristics.


    The Chicken class inherits from the Animal class. Notice the use of Animal in parentheses after the Chicken class name. This addition tells Python that Chicken is a kind of Animal, something that will inherit the characteristics of Animal.

    Notice that the Chicken constructor accepts only Name and Age. The user doesn’t have to supply a Type value because you already know that it’s a chicken. This new constructor overrides the Animal constructor. The three attributes are still in place, but Type is supplied directly in the Chicken constructor.

    Someone might try something funny, such as setting her chicken up as a gorilla. With this in mind, the Chicken class also overrides the SetType() setter. If someone tries to change the Chicken type, that user gets a message rather than the attempted change. Normally, you handle this sort of problem by using an exception, but the message works better for this example by making the coding technique clearer.


    Finally, the Chicken class adds a new feature, MakeSound(). Whenever someone wants to hear the sound a chicken makes, he can call MakeSound() to at least see it printed on the screen.


    Testing the class in an application


    Testing the Chicken class also tests the Animal class to some extent. Some functionality is different, but some classes aren’t really meant to be used. The Animal class is simply a parent for specific kinds of animals, such as Chicken. The following steps demonstrate the Chicken class so that you can see how inheritance works.
     Open a Python File window.

    You see an editor in which you can type the example code.


     Type the following code into the window — pressing Enter after each line:

    import Animals

    MyChicken = Animals.Chicken("Sally", 2) print(MyChicken) MyChicken.SetAge(MyChicken.GetAge() + 1) print(MyChicken) MyChicken.SetType("Gorilla") print(MyChicken)
    MyChicken.MakeSound()

    The first step is to import the Animals module. Remember that you always import the filename, not the class.

    The example creates a chicken, MyChicken, named Sally, who is age 2. It then starts to work with MyChicken in various ways. For example, Sally has a birthday, so the code updates Sally’s age by 1. Notice how the code combines the use of a setter, SetAge(), with a getter, GetAge(), to perform the task. After each change, the code displays the resulting object values for you. The final step is to let Sally say a few words.

     Choose RunRun Module.


    You see each of the steps used to work with MyChicken. As you can see, using inheritance can greatly simplify the task of creating new classes when enough of the classes have commonality so that you can create a parent class that contains some amount of the code.
  • Using Classes In Python - Part 4

     

    Using methods with variable argument lists


    Sometimes you create methods that can take a variable number of arguments. Handling this sort of situation is something Python does well. Here are the two kinds of variable arguments that you can create:

     *args: Provides a list of unnamed arguments.

     **kwargs: Provides a list of named arguments.

    The actual names of the arguments don’t matter, but Python developers use *args and **kwargs as a convention so that other Python developers know that they’re a variable list of arguments. Notice that the first variable argument has just one asterisk (*) associated with it, which means the arguments are unnamed. The second variable has two asterisks, which means that the arguments are named. The following steps demonstrate how to use both approaches to writing an application.
     Open a Python File window.

    You see an editor in which you can type the example code.

     Type the following code into the window — pressing Enter after each line:

    class MyClass:
    def PrintList1(*args):
    for Count, Item in enumerate(args): print("{0}. {1}".format(Count, Item))

    def PrintList2(**kwargs):
    for Name, Value in kwargs.items(): print("{0} likes {1}".format(Name, Value))

    MyClass.PrintList1("Red", "Blue", "Green")
    MyClass.PrintList2(George="Red", Sue="Blue",
    Zarah="Green")

    For the purposes of this example, you’re seeing the arguments implemented as part of a class method. However, you can use them just as easily with an instance method.

    Look carefully at PrintList1() and you see a new method of using a for loop to iterate through a list. In this case, the enumerate() function outputs both a count (the loop count) and the string that was passed to the function.


    The PrintList2() function accepts a dictionary input. Just as with PrintList1(), this list can be any length. However, you must process the items() found in the dictionary to obtain the individual values.

     Choose RunRun Module.

    The individual lists can be of any length. In fact, in this situation, playing with the code to see what you can do with it is a good idea. For example, try mixing numbers and strings with the first list to see what happens. Try adding Boolean values as well. The point is that using this technique makes your methods incredibly flexible if all you want is a list of values as input.


    Overloading operators


    In some situations, you want to be able to do something special as the result of using a standard operator such as add (+). In fact, sometimes Python doesn’t provide a default behavior for operators because it has no default to implement. No matter what the reason might be, overloading operators makes it possible to assign new functionality to existing operators so that they do what you want, rather than what Python intended. The following steps demonstrate how to overload an operator and use it as part of an application.


     Open a Python File window.

    You see an editor in which you can type the example code.


     Type the following code into the window — pressing Enter after each line:

    class MyClass:
    def __init__(self, *args): self.Input = args

    def __add__(self, Other): Output = MyClass()

    Output.Input = self.Input + Other.Input return Output

    def __str__(self): Output = ""

    for Item in self.Input: Output += Item Output += " "
    return Output

    Value1 = MyClass("Red", "Green", "Blue")
    Value2 = MyClass("Yellow", "Purple", "Cyan")
    Value3 = Value1 + Value2

    print("{0} + {1} = {2}"
    .format(Value1, Value2, Value3))

    The example demonstrates a few different techniques. The constructor, __init__(), demonstrates a method for creating an instance variable attached to the self object. You can use this approach to create as many variables as needed to support the instance.

    When you create your own classes, no + operator is defined until you define one, in most cases. The only exception is when you inherit from an existing class that already has the + operator defined. In order to add two MyClass entries together, you must define the __add__() method, which equates to the + operator.

    The code used for the __add__() method may look a little odd, too, but you need to think about it one line at a time. The code begins by creating a new object, Output, from MyClass. Nothing is added to Output at this point — it’s a blank object. The two objects that you want to add, self.Input and Other.Input, are actually tuples. The code places the sum of these two objects into Output.Input. The __add__() method then returns the new combined object to the caller.

    Of course, you may want to know why you can’t simply add the two inputs together as you would a number. The answer is that you’d end up with a tuple as an output, rather than a MyClass as an output. The type of the output would be changed, and that would also change any use of the resulting object.

    To print MyClass properly, you also need to define a __str__() method. This method converts a MyClass object into a string. In this case, the output is a space-delimited string (in which each of the items in the string is separated from the other items by a space) containing each of the values found in self.Input. Of course, the class that you create can output any string that fully represents the object.

    The main procedure creates two test objects, Value1 and Value2. It adds them together and places the result in Value3. The result is printed onscreen.

     Choose RunRun Module.

    Adding the two objects together, converting them to strings, and then printing the result. It’s a lot of code for such a simple output statement, but the result definitely demonstrates that you can create classes that are self-contained and fully functional.

    Creating a Class


    All the previous material in this Post has helped prepare you for creating an interesting class of your own. In this case, you create a class that you place into an external module and eventually access within an application. Listing 14-1 shows the code that you need to create the class.


    Listing 14-1:  Creating an External Class

    class MyClass:
    def __init__(self, Name="Sam", Age=32): self.Name = Name
    self.Age = Age

    def GetName(self): return self.Name

    def SetName(self, Name): self.Name = Name

    def GetAge(self): return self.Age

    def SetAge(self, Age): self.Age = Age

    def __str__(self):

    return "{0} is aged {1}.".format(self.Name, self.Age)


    In this case, the class begins by creating an object with two instance variables: Name and Age. If the user fails to provide these values, they default to Sam and 32.


    This example provides you with a new class feature. Most developers call this feature an accessor. Essentially, it provides access to an underlying value. There are two types of accessors: getters and setters. Both GetName() and GetAge() are getters. They provide read-only access to the underlying value. The SetName() and SetAge() methods are setters, which provide write-only access to the underlying value. Using a combination of methods like this allows you to check inputs for correct type and range, as well as verify that the caller has permission to view the information.


    As with just about every other class you create, you need to define the __str__() method if you want the user to be able to print the object. In this case, the class provides formatted output that lists both of the instance variables.
  • Using Classes In Python - Part 3



    Working with constructors

     

    A constructor is a special kind of method that Python calls when it instantiates an object using the definitions found in your class. Python relies on the constructor to perform tasks such as initializing (assigning values to) any instance variables that the object will need when it starts. Constructors can also verify that there are enough resources for the object and perform any other start-up task you can think of.


    The name of a constructor is always the same, __init__(). The constructor can accept arguments when necessary to create the object. When you create a class without a constructor, Python automatically creates a default constructor for you that doesn’t do anything. Every class must have a constructor, even if it simply relies on the default constructor. The following steps demonstrate how to create a constructor:


    Open a Python Shell window.

    You see the familiar Python prompt.

    Type the following code (pressing Enter after each line and pressing Enter twice after the last line):

    class MyClass: Greeting = ""

    def __init__(self, Name="there"): self.Greeting = Name + "!"

    def SayHello(self):
    print("Hello {0}".format(self.Greeting))

    This example provides your first example of function overloading. In this case, there are two versions of __init__(). The first doesn’t require any special input because it uses the default value for the Name of "there". The second requires a name as an input. It sets Greeting to the value of this name, plus an exclamation mark.

    Python doesn’t support true function overloading. Many strict adherents to strict Object-Oriented Programming (OOP) principles consider default values to be something different from function overloading.

    However, the use of default values obtains the same result, and it’s the only option that Python offers. In true function overloading, you see multiple copies of the same function, each of which could process the input differently.

     Type MyInstance = MyClass( ) and press Enter.

    Python creates an instance of MyClass named MyInstance.

     Type MyInstance.SayHello( ) and press Enter.

    Notice that this message provides the default, generic greeting.

     Type MyInstance = MyClass(“Amy”) and press Enter. Python creates an instance of MyClass named MyInstance.

     Type MyInstance.SayHello( ) and press Enter.

    Notice that this message provides a specific greeting.

     Close the Python Shell window.

    Working with variables


    As mentioned earlier in the book, variables are storage containers that hold data. When working with classes, you need to consider how the data is stored and managed. A class can include both class variables and instance variables. The class variables are defined as part of the class itself, while instance variables are defined as part of methods. The following sections show how to use both variable types.

     

    Creating class variables


    Class variables provide global access to data that your class manipulates in some way. In most cases, you initialize global variables using the constructor to ensure that they contain a known good value. The following steps demonstrate how class variables work.

     Open a Python Shell window.

    You see the familiar Python prompt.

     Type the following code (pressing Enter after each line and pressing Enter twice after the last line):

    class MyClass: Greeting = ""

    def SayHello(self):
    print("Hello {0}".format(self.Greeting))

    . Normally you do include a constructor to ensure that the class variable is initialized properly. However, this series of steps shows how class variables can go wrong.

     Type MyClass.Greeting = “Zelda” and press Enter.

    This statement sets the value of Greeting to something other than the value that you used when you created the class. Of course, anyone could make this change. The big question is whether the change will take.

     Type MyClass.Greeting and press Enter.

    You see that the value of Greeting has changed.

     Type MyInstance = MyClass( ) and press Enter.

    Python creates an instance of MyClass named MyInstance.

     Type MyInstance.SayHello( ) and press Enter.

    The change that you made to Greeting has carried over to the instance of the class. It’s true
    that the use of a class variable hasn’t really caused a problem in this example, but you can imagine what would happen in a real application if someone wanted to cause problems.

    This is just a simple example of how class variables can go wrong. The two concepts you should take away from this example are as follows:

     Avoid class variables when you can because they’re inherently unsafe.

     Always initialize class variables to a known good value in the constructor code.

     Close the Python Shell window.


    Creating instance variables


    Instance variables are always defined as part of a method. The input arguments to a method are considered instance variables because they exist only when the method exists. Using instance variables is usually safer than using class variables because it’s easier to maintain control over them and to ensure that the caller is providing the correct input. The following steps show an example of using instance variables.


     Open a Python Shell window.

    You see the familiar Python prompt.

     Type the following code (pressing Enter after each line and pressing Enter twice after the last line):

    class MyClass:

    def DoAdd(self, Value1=0, Value2=0): Sum = Value1 + Value2
    print("The sum of {0} plus {1} is {2}."
    .format(Value1, Value2, Sum))

    In this case, you have three instance variables. The input arguments, Value1 and Value2, have default values of 0, so DoAdd() can’t fail simply because the user forgot to provide values. Of course, the user could always supply something other than numbers, so you should provide the appropriate checks as part of your code. The third instance variable is Sum, which is equal to Value1 + Value2. The code simply adds the two numbers together and displays the result.

     Type MyInstance = MyClass( ) and press Enter.

    Python creates an instance of MyClass named MyInstance.

     Type MyInstance.DoAdd(1, 4) and press Enter.

    5. Close the Python Shell window.



  • Using Classes In Python - Part 2


    Creating the class definition

    A class need not be particularly complex. In fact, you can create just the container and one class element and call it a class. Of course, the resulting class won’t do much, but you can instantiate it (tell Python to build an object using your class as a blueprint) and work with it as you would any other class. The following steps help you understand the basics behind a class by creating the simplest class possible.


    Open a Python Shell window.

    You see the familiar Python prompt.

     Type the following code (pressing Enter after each line and pressing Enter twice after the last line):

    class MyClass: MyVar = 0

    The first line defines the class container, which consists of the keyword class and the class name, which is MyClass. Every class you create must begin precisely this way. You must always include class followed by the class name.

    The second line is the class suite. All the elements that comprise the class are called the class suite. In this case, you see a class variable named MyVar, which is set to a value of 0. Every instance of the class will have the same variable and start at the same value.

     Type MyInstance = MyClass( ) and press Enter.

    You have just created an instance of MyClass named MyInstance. Of course, you’ll want to verify that you really have created such an instance. Step 4 accomplishes that task.

     Type MyInstance.MyVar and press Enter.

    The output of 0, demonstrates that MyInstance does indeed have a class variable named MyVar.



     Type MyInstance.__class__ and press Enter.

    Python displays the class used to create this instance. The output tells you that this class is part of the __main__ module, which means that you typed it directly into the shell.


    6. Retain this window and class for the next section.

     

    Considering the built-in class attributes


    When you create a class, you can easily think that all you get is the class. However, Python adds built-in functionality to your class. For example, in the preceding section, you type __class__ and press Enter. The __class__ attribute is built in; you didn’t create it. It helps to know that Python provides this functionality so that you don’t have to add it. The functionality is needed often enough that every class should have it, so Python supplies it. The following steps help you work with the built-in class attributes.

     Use the Python Shell window that you open in the preceding section.

    If you haven’t followed the steps in the preceding section, “Creating the class definition,” please do so now.

     Type dir(MyInstance) and press Enter.

    A list of attributes appears. These attributes provide specific functionality for your class. They’re also common to every other class you create, so you can count on always having this functionality in the classes you create.

     Type help(‘__class__’) and press Enter.

    Python displays information on the __class__ attribute. You can use the same technique for learning more about any attribute that Python adds to your class.

     Close the Python Shell window.

    Working with methods


    Methods are simply another kind of function that reside in classes. You create and work with methods in precisely the same way that you do functions, except that methods are always associated with a class (you don’t see freestanding methods as you do functions). You can create two kinds of methods: those associated with the class itself and those associated with an instance of a class. It’s important to differentiate between the two. The following sections provide the details needed to work with both.


    Creating class methods


    A class method is one that you execute directly from the class without creating an instance of the class. Sometimes you need to create methods that execute from the class, such as the functions you used with the str class in order to modify strings. The following steps demonstrate how to create and use a class method.

     Open a Python Shell window.

    You see the familiar Python prompt.

     Type the following code (pressing Enter after each line and pressing Enter twice after the last line):

    class MyClass: def SayHello():
    print("Hello there!")

    The example class contains a single defined attribute, SayHello(). This method doesn’t accept any arguments and doesn’t return any values. It simply prints a message as output. However, the method works just fine for demonstration purposes.

     Type MyClass.SayHello() and press Enter.

    The example outputs the expected string. Notice that you didn’t need to create an instance of the class — the method is available immediately for use.

     Close the Python Shell window.


    A class method can work only with class data. It doesn’t know about any data associated with an instance of the class. You can pass it data as an argument, and the method can return information as needed, but it can’t access the instance data. As a consequence, you need to exercise care when creating class methods to ensure that they’re essentially self-contained.

    Creating instance methods


    An instance method is one that is part of the individual instances. You use instance methods to manipulate the data that the class manages. As a consequence, you can’t use instance methods until you instantiate an object from the class.


    All instance methods accept a single argument as a minimum, self. The self-argument points at the particular instance that the application is using to manipulate data. Without the self-argument, the method wouldn’t know which instance data to use. However, self isn’t considered an accessible argument — the value for self is supplied by Python, and you can’t change it as part of calling the method.

    The following steps demonstrate how to create and use instance methods in Python.


     Open a Python Shell window.

    You see the familiar Python prompt.

     Type the following code (pressing Enter after each line and pressing Enter twice after the last line):

    class MyClass:
    def SayHello(self): print("Hello there!")

    The example class contains a single defined attribute, SayHello(). This method doesn’t accept any special arguments and doesn’t return any values. It simply prints a message as output. However, the method works just fine for demonstration purposes.

     Type MyInstance = MyClass( ) and press Enter.

    Python creates an instance of MyClass named MyInstance.

     Type MyInstance.SayHello( ) and press Enter.


    5. Close the Python Shell window.
  • Using Classes In Python - Part 1

    Using Classes in Python

    You’ve already worked with a number of classes in previous Posts. Many of the examples are easy to construct and use because they depend on the Python classes. Even though classes are briefly mentioned in previous Posts, those Posts largely ignore them simply because discussing them wasn’t immediately important.

    Classes make working with Python code more convenient by helping to make your applications easy to read, understand, and use. You use classes to create containers for your code and data, so they stay together in one piece. Outsiders see your class as a black box — data goes in and results come out.

    At some point, you need to start constructing classes of your own if you want to avoid the dangers of the spaghetti code that is found in older applications. Spaghetti code is much as the name implies — various lines of procedures are interwoven and spread out in such a way that it’s hard to figure out where one piece of spaghetti begins and another ends. Trying to maintain spaghetti code is nearly impossible, and some organizations have thrown out applications because no one could figure them out.

    Besides helping you understand classes as a packaging method that avoids spaghetti code, this Post helps you create and use your own classes for the first time. You gain insights into how Python classes work toward making your applications convenient to work with. This is an introductory sort of Post, though, and you won’t become so involved in classes that your head begins to spin around on its own. This Post is about making class development simple and manageable.


    Understanding the Class as a Packaging Method


    A class is essentially a method for packaging code. The idea is to simplify code reuse, make applications more reliable, and reduce the potential for security breaches. Well-designed classes are black boxes that accept certain inputs and provide specific outputs based on those inputs. In short, a class shouldn’t create any surprises for anyone and should have known (quantifiable) behaviors. How the class accomplishes its work is unimportant, and hiding the details of its inner workings is essential to good coding practice.

    Before you move onto actual class theory, you need to know a few terms that are specific to classes. The following list defines terms that you need to know in order to use the material that follows later in the Post. These terms are specific to Python. (Other languages may use different terms for the same techniques or define terms that Python uses in different ways.)

    Class: Defines a blueprint for creating an object. Think of a builder who wants to create a building of some type. The builder uses a blueprint to ensure that the building will meet the required specifications. Likewise, Python uses classes as a blueprint for creating new objects.

    Class variable: Provides a storage location used by all methods in an instance of the class. A class variable is defined within the class proper but outside of any of the class methods. Class variables aren’t used very often because they’re a potential security risk — every method of the class has access to the same information. In addition to being a security risk, class variables are also visible as part of the class rather than a particular instance of a class, so they pose the potential problem of class contamination.

     Data member: Defines either a class variable or an instance variable used to hold data associated with a class and its objects.

     Function overloading: Creates more than one version of a function, which results in different behaviors. The essential task of the function may be the same, but the inputs are different and potentially the outputs as well. Function overloading is used to provide flexibility so that a function can work with applications in various ways.

     Inheritance: Uses a parent class to create child classes that have the same characteristics. The child classes usually have extended functionality or provide more specific behaviors than the parent class does.

    Instance: Defines an object created from the specification provided by a class. Python can create as many instances of a class to perform the work required by an application. Each instance is unique.

     Instance variable: Provides a storage location used by a single method of an instance of a class. The variable is defined within a method.
    Instance variables are considered safer than class variables because only one method of the class can access them. Data is passed between methods using arguments, which allows for controlled checks of incoming data and better control over data management.

    Instantiation: Performs the act of creating an instance of a class. The resulting object is a unique class instance.

     Method: Defines the term used for functions that are part of a class. Even though function and method essentially define the same element, method is considered more specific because only classes can have methods.

     Object: Defines a unique instance of a class. The object contains all the methods and properties of the original class. However, the data for each object differs. The storage locations are unique, even if the data is the same.

     Operator overloading: Creates more than one version of a function that is associated with an operator such as: +, -, /, or *, which results in different behaviors. The essential task of the operator may be the same, but the way in which the operator interacts with the data differs. Operator overloading is used to provide flexibility so that an operator can work with applications in various ways.


    Considering the Parts of a Class


    A class has a specific construction. Each part of a class performs a particular task that gives the class useful characteristics. Of course, the class begins with a container that is used to hold the entire class together, so that’s the part that the first section that follows discusses. The remaining sections describe the other parts of a class and help you understand how they contribute to the class as a whole.
  • Facing Errors In Python - Part 6




    Passing error information to the caller

    Python provides exceptionally flexible error handling in that you can pass information to the caller (the code that is calling your code) no matter which exception you use. Of course, the caller may not know that the information is available, which leads to a lot of discussion on the topic. If you’re working with someone else’s code and don’t know whether additional information is available, you can always use the technique described in the “Obtaining a list of exception arguments” sidebar earlier in this Post to find it.

    You may have wondered whether you could provide better information when working with a ValueError exception than with an exception provided natively by Python. The following steps show that you can modify the output so that it does include helpful information.

     Open a Python File window.

    You see an editor in which you can type the example code.

     Type the following code into the window — pressing Enter after each line:

    try:
    Ex = ValueError()
    Ex.strerror = "Value must be within 1 and 10." raise Ex
    except ValueError as e:
    print("ValueError Exception!", e.strerror)


    The ValueError exception normally doesn’t provide an attribute named strerror (a common name for string error), but you can add it simply by assigning a value to it as shown. When the example raises the exception, the except clause handles it as usual but obtains access to the attributes using e. You can then access the e.strerror member to obtain the added information.

     Choose RunRun Module.

    You see a Python Shell window open. The application displays an expanded ValueError exception

    Creating and Using Custom Exceptions

    Python provides a wealth of standard exceptions that you should use when-ever possible. These exceptions are incredibly flexible, and you can even modify them as needed (within reason) to meet specific needs. For example, the “Passing error information to the caller” section of this Post demon-strates how to modify a ValueError exception to allow for additional data. However, sometimes you simply must create a custom exception because none of the standard exceptions will work. Perhaps the exception name just doesn’t tell the viewer the purpose that the exception serves. You may need a custom exception for specialized database work or when working with a service.

    The example in this section shows a quick method for creating your own exceptions. To perform this task, you must create a class that uses an exist-ing exception as a starting point. To make things a little easier, this example creates an exception that builds upon the functionality provided by the ValueError exception. The advantage of using this approach rather than the one shown in the “Passing error information to the caller” section, the preceding section in this Post, is that this approach tells anyone who follows you precisely what the addition to the ValueError exception is; additionally, it makes the modified exception easier to use.

     Open a Python File window.

    You see an editor in which you can type the example code.

     Type the following code into the window — pressing Enter after each line:

    class CustomValueError(ValueError): def __init__(self, arg):

    self.strerror = arg self.args = {arg}

    try:

    raise CustomValueError("Value must be within 1 and 10.")
    except CustomValueError as e: print("CustomValueError Exception!", e.strerror)

    This example essentially replicates the functionality of the example in the “Passing error information to the caller” section of the Post. However, it places the same error in both strerror and args so that the developer has access to either (as would normally happen).

    The code begins by creating the CustomValueError class that uses the ValueError exception class as a starting point. The __init__() func-tion provides the means for creating a new instance of that class. Think of the class as a blueprint and the instance as the building created from the blueprint.

    Notice that the strerror attribute has the value assigned directly to it, but args receives it as an array. The args member normally contains an array of all the exception values, so this is standard procedure, even when args contains just one value as it does now.

    The code for using the exception is considerably easier than modify-ing ValueError directly. All you do is call raise with the name of the exception and the arguments you want to pass, all on one line.


     Choose RunRun Module.

    You see a Python Shell window open. The application displays the letter sequence, along with the letter number.

    Using the finally Clause

    Normally you want to handle any exception that occurs in a way that doesn’t cause the application to crash. However, sometimes you can’t do anything to fix the problem, and the application is most definitely going to crash. At this point, your goal is to cause the application to crash gracefully, which means closing files so that the user doesn’t lose data and performing other tasks of that nature. Anything you can do to keep damage to data and the system to a minimum is an essential part of handling data for a crashing application.

    The finally clause is part of the crashing-application strategy. You use this clause to perform any required last-minute tasks. Normally, the finally clause is quite short and uses only calls that are likely to succeed without further problem. It’s essential to close the files, log the user off, and per-form other required tasks, and then let the application crash before some-thing terrible happens (such as a total system failure). With this necessity in mind, the following steps show a simple example of using the finally clause.

     Open a Python File window.

    You see an editor in which you can type the example code.


     Type the following code into the window — pressing Enter after each line:

    import sys

    try:
    raise ValueError print("Raising an exception.")

    except ValueError: print("ValueError Exception!") sys.exit()
    finally:
    print("Taking care of last minute details.")

    print("This code will never execute.")

    In this example, the code raises a ValueError exception. The except clause executes as normal when this happens. The call to sys.exit() means that the application exits after the exception is handled. Perhaps the application can’t recover in this particular instance, but the applica-tion normally ends, which is why the final print() function call won’t ever execute.

    The finally clause code always executes. It doesn’t matter whether the exception happens or not. The code you place in this block needs to be common code that you always want to execute. For example, when working with a file, you place the code to close the file into this block to ensure that the data isn’t damaged by remaining in memory rather than going to disk.

     Choose RunRun Module.

    You see a Python Shell window open. The application displays the except clause message and the finally clause message. The sys.exit() call prevents any other code from executing.

     Comment out the raise ValueError call by preceding it with two pound signs, like this:

    ##raise ValueError

    Removing the exception will demonstrate how the finally clause actually works.

     Save the file to disk to ensure that Python sees the change.

     Choose RunRun Module.

    You see a Python Shell window open. The application displays a series of messages, including the finally clause message. This part of the example shows that the finally clause always executes, so you need to use it carefully.


  • Facing Errors In Python - Part 5

    Handling more specific to less specific exceptions

    One strategy for handling exceptions is to provide specific except clauses for all known exceptions and generic except clauses to handle unknown exceptions. You can see the exception hierarchy that Python uses at https://docs.python.org/3.3/library/exceptions. html#exception-hierarchy. When viewing this chart, BaseException is the uppermost exception. Most exceptions are derived from Exception. When working through math errors, you can use the generic
    ArithmeticError or a more specific ZeroDivisionError exception.

    Python evaluates except clauses in the order in which they appear in the source code file. The first clause is examined first, the second clause is exam-ined second, and so on. The following steps help you examine an example that demonstrates the importance of using the correct exception order. In this case, you perform tasks that result in math errors.

     Open a Python File window.

    You see an editor in which you can type the example code.

     Type the following code into the window — pressing Enter after each line:

    try:

    Value1 = int(input("Type the first number: ")) Value2 = int(input("Type the second number: ")) Output = Value1 / Value2
    except ValueError:
    print("You must type a whole number!") except KeyboardInterrupt:
    print("You pressed Ctrl+C!") except ArithmeticError:

    print("An undefined math error occurred.") except ZeroDivisionError:
    print("Attempted to divide by zero!") else:
    print(Output)

    The code begins by obtaining two inputs: Value1 and Value2. The first two except clauses handle unexpected input. The second two except clauses handle math exceptions, such as dividing by zero. If everything goes well with the application, the else clause executes, which prints the result of the operation.

     Choose RunRun Module.

    You see a Python Shell window open. The application asks you to type the first number.

     Type Hello and press Enter.

    As expected, Python displays the ValueError exception message. However, it always pays to check for potential problems.

     Choose RunRun Module again.

    You see a Python Shell window open. The application asks you to type the first number.

     Type 8 and press Enter.

    The application asks you to enter the second number.


     Type 0 and press Enter.

    You see the error message for the ArithmeticError exception. What you should actually see is the ZeroDivisionError exception because it’s more specific than the ArithmeticError exception.

    8. Reverse the order of the two exceptions so that they look like this:

    except ZeroDivisionError: print("Attempted to divide by zero!")
    except ArithmeticError:
    print("An undefined math error occurred.")

     Perform Steps 5 through 7 again.

    This time, you see the ZeroDivisionError exception message because the exceptions appear in the correct order.

     Perform Steps 5 through 7 again, but type 2 for the second number instead of 0.

    This time, the application finally reports an output value of 4.0. Division results in a floating-point value unless you specify that you want an integer output by using the floor division operator (//).


    Nested exception handling

    Sometimes you need to place one exception-handling routine within another in a process called nesting. When you nest exception-handling routines, Python tries to find an exception handler in the nested level first and then moves to the outer layers. You can nest exception-handling routines as deeply as needed to make your code safe.


    One of the more common reasons to use a dual layer of exception-handling code is when you want to obtain input from a user and need to place the input code in a loop to ensure that you actually get the required information. The following steps demonstrate how this sort of code might work.

    Open a Python File window.

    You see an editor in which you can type the example code.


     Type the following code into the window — pressing Enter after each line:

    TryAgain = True

    while TryAgain:

    try:

    Value = int(input("Type a whole number. ")) except ValueError:
    print("You must type a whole number!")

    try:

    DoOver = input("Try again (y/n)? ") except:
    print("OK, see you next time!") TryAgain = False
    else:
    if (str.upper(DoOver) == "N"): TryAgain = False

    except KeyboardInterrupt: print("You pressed Ctrl+C!") print("See you next time!") TryAgain = False
    else:

    print(Value) TryAgain = False

    The code begins by creating an input loop. Using loops for this type of purpose is actually quite common in applications because you don’t want the application to end every time an input error is made. This is a simpli-fied loop, and normally you create a separate function to hold the code.

    When the loop starts, the application asks the user to type a whole number. It can be any integer value. If the user types any non-integer value or presses Ctrl+C, Cmd+C, or another interrupt key combination, the exception-handling code takes over. Otherwise, the application prints the value that the user supplied and sets TryAgain to False, which causes the loop to end.

    A ValueError exception can occur when the user makes a mistake. Because you don’t know why the user input the wrong value, you have to ask if the user wants to try again. Of course, getting more input from the user could generate another exception. The inner try . . . except code block handles this secondary input.

    Notice the use of the str.upper() function when getting character input from the user. This function makes it possible to receive y or Y as input and accept them both. Whenever you ask the user for character input, it’s a good idea to convert lowercase characters to uppercase so that you can perform a single comparison (reducing the potential for error).

    The KeyboardInterrupt exception displays two messages and then exits automatically by setting TryAgain to False. The KeyboardInterrupt occurs only when the user presses a specific key combination designed to end the application. The user is unlikely to want to continue using the application at this point.

     Choose RunRun Module.

    You see a Python Shell window open. The application asks the user to input a whole number.

     Type Hello and press Enter.

    The application displays an error message and asks whether you want to try again.

     Type Y and press Enter.

    The application asks you to input a whole number again.

     Type 5.5 and press Enter.

    The application again displays the error message and asks whether you want to try again.

     Press Ctrl+C, Cmd+C, or another key combination to interrupt the application.

    The application ends. Notice that the message is the one from the inner exception. The application never gets to the outer exception because the inner exception handler provides generic exception handling.

     Choose RunRun Module.

    You see a Python Shell window open. The application asks the user to input a whole number.


     Press Ctrl+C, Cmd+C, or another key combination to interrupt the application.

    The application ends. Notice that the message is the one from the outer exception. In Steps 7 and 9, the user ends the application by pressing an interrupt key. However, the application uses two different exception handlers to address the problem.

    Raising Exceptions

    So far, the examples in this Post have reacted to exceptions. Something happens and the application provides error-handling support for that event. However, situations arise for which you may not know how to handle an error event during the application design process. Perhaps you can’t even handle the error at a particular level and need to pass it up to some other level to handle. In short, in some situations, your application must generate an excep-tion. This act is called raising (or sometimes throwing) the exception. The fol-lowing sections describe common scenarios in which you raise exceptions in specific ways.



    Raising exceptions during exceptional conditions

    The example in this section demonstrates how you raise a simple exception­ — that it doesn’t require anything special. The following steps simply create the exception and then handle it immediately.

    Open a Python File window.

    You see an editor in which you can type the example code.

     Type the following code into the window — pressing Enter after each line:

    try:
    raise ValueError except ValueError:
    print("ValueError Exception!")

    You wouldn’t ever actually create code that looks like this, but it shows you how raising an exception works at its most basic level. In this case, the raise call appears within a try . . . except block. A basic raise call simply provides the name of the exception to raise (or throw). You can also provide arguments as part of the output to provide additional information.

    Notice that this try . . . except block lacks an else clause because there is nothing to do after the call. Although you rarely use a try . . . except block in this manner, you can. You may encounter situations like this one sometimes and need to remember that adding the else clause is purely optional. On the other hand, you must add at least one except clause.

     Choose RunRun Module.

    You see a Python Shell window open. The application displays the expected exception text.



  • Powered by Blogger.

    Tags

    Popular Posts