# Outcome Class¶

In Outcome Analysis we’ll look at the responsibilities and collaborators of Outcome objects.

In Design Decision – Object Identity we’ll look at how we can implement the notion of object identity and object equality. This is important because we will be matching Outcome objects based on bets and spinning the Roulette wheel.

We’ll look forward to some other use cases in Looking Forward. Specifically, we know that players, games, and tables will all share references to single outcome objects. How do we do this properly?

In Outcome Design we’ll detail the design for this class. In Outcome Deliverables we’ll provide a list of modules that must be built.

We’ll look at a Python programming topic in Message Formatting. This is a kind of appendix for beginning programmers.

## Outcome Analysis¶

There will be several hundred instances of `Outcome` objects on a given Roulette table. The bins on the wheel, similarly, collect various `Outcome`s together. The minimum set of `Outcome` instances we will need are the 38 numbers, Red, and Black. The other instances will add details to our simulation.

In Roulette, the amount won is a simple multiplication of the amount bet and the odds. In other games, however, there may be a more complex calculation because the house keeps 5% of the winnings, called the “rake”. While it is not part of Roulette, it is good to have our `Outcome` class designed to cope with these more complex payout rules.

Also, we know that other casino games, like Craps, are stateful. An `Outcome` may change the game state. We can foresee reworking this class to add in the necessary features to change the state of the game.

While we are also aware that some odds are not stated as , we won’t include these other kinds of odds in this initial design. Since all Roulette odds are , we’ll simply assume that the denominator is always 1. We can forsee reworking this class to handle more complex odds, but we don’t need to handle the other cases yet.

The issue we have, however, is comparing outcomes. They’re used in various places. The idea is to compare the outcomes from a spin of the wheel against the outcomes associated with bets.

How does all this comparison work in Python?

Hint: The default rules aren’t helpful.

## Design Decision – Object Identity¶

Our design will depend on matching `Outcome` objects. We’ll be testing objects for equality.

The player will be placing bets that contain `Outcome`s; the table will be holding bets. The wheel will select the winning `Outcome`s. We need a simple test to see if two objects of the `Outcome` class are the same.

Was the `Outcome` for a bet equal to the `Outcome` contained in a spin of the wheel?

It turns out that this comparison between objects has some subtlety to it.

Here’s the naïve approach to class definition that doesn’t include any provision for equality tests.

Naïve Class Definition

```>>> class Outcome:
...    def __init__(self, name, odds):
...        self.name= name
...        self.odds= odds
```

This seems elegant enough. Sadly, it doesn’t work out when we need to make equality tests.

In Python, if we do nothing special, the `__eq__()` test will simply compare the internal object id values. These object id values are unique to each distinct object, irrespective of the attribute values.

This default behavior of objects is shown by the following example:

Equality Test Failure

```>>> oc1= Outcome( "Any Craps", 8 )
>>> oc2= Outcome( "Any Craps", 8 )
>>> oc1 == oc2
False
>>> id(oc1)
4334572936
>>> id(oc2)
4334573272
```

Note

Exact ID values will vary.

This example shows that we can have two objects that appear equal, but don’t compare as equal. They are distinct objects with the same attribute values. This makes them not equal according to the default methods inherited from `object`. However, we would like to have two of these objects test as equal.

Actually, we want more than that.

### More than equal¶

We’ll be creating collections of `Outcome` objects, and we may need to create sets or maps where hash codes are used in addition to the simple equality tests.

Hash Codes?

Every object has a hash code. The hash code is simply an integer. It can be a summary of the bits that make up the object. It may simply be based on the internal id value for the object. Python computes hash codes and uses these as a quick test for set membership and dictionary keys.

If two hash codes don’t match, the objects can’t possibly be equal. Further comparisons aren’t necessary. If two hash codes do match, then it’s worth the investment of time to do use the more detailed equality comparison.

As we look forward, the Python `set` and `dict` depend on a `__hash__()` method and an `__eq__()` method of each object in the collection.

Hash Code Failure

```>>> hash(oc1)
270386794
>>> hash(oc2)
270392959
```

Note

Exact ID values will vary.

This shows that two objects that look the same to us can have distinct hash codes. Clearly, this is unacceptable, since we want to be able to create a set of `Outcome` objects without having things that look like repeats.

### Layers of Meaning¶

The issue is that we have three distinct layers of meaning for comparing objects to see if they are “equal”.

• Have the same hash code. We can call this “hash equality”.

This means the `__hash__(self)()` method for several objects that represent the same `Outcome` must also have the same hash code. When we put an object into a set or a dictionary, Python uses the `hash()` function which is implemented by the `__hash__()` method.

Sometimes the hash codes are equal, but the object attributes aren’t actually equal. This is called a hash collision, and it’s rare but not unexpected.

If we don’t implement this, the default version isn’t too useful for creating sets of our `Outcome` obejcts.

• Compare as Equal. We can call this “attribute equality”.

This means that the `__eq__()` method returns True. When we use the == operator, this is evaluated by using the `__eq__()` method. This must be overridden by a class to implement attribute equality.

If we don’t implement this, the default version isn’t too useful for our `Outcome` obejcts.

• Are references to the same object. We can call this “identity”.

We can test that two objects are the same by using the is comparison between two objects. This uses the internal Python identifier for each object. The identifier is revealed by the `id()` function.

When we use the is comparison, we’re asserting that the two variables are references to the same underlying object. This is the identity comparison.

### Basics of Equality¶

We note that each instance of `Outcome` has a distinct `Outcome.name` value, it seems simple enough to compare names. This is one sense of “equal” that seems to be appropriate.

We can define the `__eq__()` and `__ne__()` methods work two ways:

• When comparing `Outcome` and string, it will compares the `Outcome.name` attribute. This is easy, lazy and seems to work perfectly.
• When comparing `Outcome` and `Outcome`, it can compare both name and odds. This seems like over-engineering. The odds depend on the name. The name is the key, the odds are just an attribute.

Similarly, we can compute the value of `__hash__()` using only the string name, and not the odds. This seems elegantly simple to return the hash of the string name rather than compute a hash.

The definition for `__hash__()` in section 3.3.1 of the Language Reference Manual tells us to do the calculation using a modulus based on `sys.hash_info.width`. This is the number of bits, the actual value we want to use is We’d use `sys.hash_info.modulus`.

## Looking Forward¶

We’ll be looking at `Outcome` objects in several contexts.

• We’ll have them in bins of a wheel as winning outcomes from each spin of the wheel.
• We’ll have them in bets that have been placed on the table.
• They player will have some outcomes that they prefer to bet on.

We’ll be comparing table bet outcomes and bin outcomes for equality. We have a solution to that, above.

We’ll be creating outcome objects, too. This bumps into an interesting problem.

How do we maintain Don’t Repeat Yourself (DRY) when creating `Outcome` objects?

We don’t want to include the odds every time we create an `Outcome`. Repeating the odds would violate the DRY principle.

What are some alternatives?

• Global Outcome Objects. We can declare global variables for the various outcomes and use those global objects as needed.

Generally, globals variables are often undesirable because changes to those variables can have unexpected consequences in a large application.

Global constants are no problem at all. The pool of `Outcome` instances are proper constant values used to create bins and bets. There would be a lot of them, and they would all be assigned to distinct variables. This sounds complicated.

• Outcome Factory. We can create a function which is a Factory for individual `Outcome` objects.

When some part of the application needs an `Outcome` object, the factory will do one of two things. If the object doesn’t yet exist, the Factory would create it, save it, and return a reference to it. When some part of the application asked for an `Outcome` which already exists, the Factory would return a reference to the existing object.

This centralizes the pool of global objects into a single object, the Factory.

Further, we can identify `Outcome` instances by their names, and avoid repeating the payout odds. The function would map a name of an `Outcome` the object with all of it’s details.

As a practical matter, the Factory could be seeded with all outcomes. The factory function is – in effect – a global pool of constant objects.

• Singleton Outcome Class. A Singleton class creates and maintains a single instance of itself. This requires that the class have a static `instance()` method that is a reference to the one-and-only instance of the class.

This saves us from creating global variables. Instead, each class definition contains it’s own private reference to the one-and-only object of that class.

However, this has the profound disadvantage that each distinct outcome would need to be a distinct subclass of `Outcome`. This is an unappealing level of complexity. Further, it doens’t solve the DRY problem of repeating the details of each Outcome.

A Factory seems like a good way to proceed. It can maintain a collection, and provide values from that collection. We can use class strings to identify `Outcome` objects. We don’t have to repeat the odds.

We’ll look forward to this in subsequent exercises. For now, we’ll start with the basic class.

## Outcome Design¶

class `Outcome`

`Outcome` contains a single outcome on which a bet can be placed.

In Roulette, each spin of the wheel has a number of `Outcome`s with bets that will be paid off.

For example, the “1” bin has the following winning `Outcome`s: “1”, “Red”, “Odd”, “Low”, “Column 1”, “Dozen 1-12”, “Split 1-2”, “Split 1-4”, “Street 1-2-3”, “Corner 1-2-4-5”, “Five Bet”, “Line 1-2-3-4-5-6”, “00-0-1-2-3”, “Dozen 1”, “Low” and “Column 1”. All of these bets will payoff if the wheel spins a “1”.

### Fields¶

`Outcome.``name`

Holds the name of the `Outcome`. Examples include `"1"`, `"Red"`.

`Outcome.``odds`

Holds the payout odds for this `Outcome`. Most odds are stated as 1:1 or 17:1, we only keep the numerator (17) and assume the denominator is 1.

We can use `name` to provide hash codes and do equality tests.

### Constructors¶

`Outcome.``__init__`(self, name, odds)
Parameters: name (str) – The name of this outcome odds (int) – The payout odds of this outcome.

Sets the instance name and odds from the parameter name and odds.

### Methods¶

For now, we’ll assume that we’re going to have global instances of each `Outcome`. Later we’ll introduce some kind of Factory.

`Outcome.``winAmount`(self, amount) → amount

Multiply this `Outcome`‘s odds by the given amount. The product is returned.

Parameters: amount (number) – amount being bet
`Outcome.``__eq__`(self, other) → boolean

Compare the `name` attributes of `self` and `other`.

Parameters: other (Outcome) – Another `Outcome` to compare against. True if this name matches the other name. bool
`Outcome.``__ne__`(self, other) → boolean

Compare the `name` attributes of `self` and `other`.

Parameters: other (Outcome) – Another `Outcome` to compare against. True if this name does not match the other name. bool
`Outcome.``__hash__`(self) → int

Hash value for this outcome.

Returns: The hash value of the name, `hash(self.name)`. int

A hash calculation must include all of the attributes of an object that are essential to it’s distinct identity.

In this case, we can return `hash(self.name)` because the odds aren’t really part of what makes an outcome distinct. Each outcome is an abstraction and a string name is all that identifies them.

The definition for `__hash__()` in section 3.3.1 of the Language Reference Manual tells us to do the calculation using a modulus based on `sys.hash_info.width`. That value is the number of bits, the actual value we want to use is `sys.hash_info.modulus`, which is based on the width.

`Outcome.``__str__`(self) → string

Easy-to-read representation of this outcome. See Message Formatting.

This easy-to-read String output method is essential. This should return a `String` representation of the name and the odds. A form that looks like `1-2 Split (17:1)` works nicely.

Returns: String of the form `name (odds:1)`. str
`Outcome.``__repr__`(self) → string

Detailed representation of this outcome. See Message Formatting.

Returns: String of the form `Outcome(name, odds)`. str

## Outcome Deliverables¶

There are two deliverables for this exercise. Both will have Python docstrings.

• The `Outcome` class.

• Unit tests of the `Outcome` class. This can be doctest strings inside the class itself, or it can be a separate `unittest.TestCase` class.

The unit test should create a three instances of `Outcome`, two of which have the same name. It should use a number of individual tests to establish that two `Outcome` with the same name will test true for equality, have the same hash code, and establish that the `winAmount()` method works correctly.

## Message Formatting¶

For the very-new-to-Python, there are few variations on creating a formatted string.

Generally, we simply use something like this.

```def __str__( self ):
return "{name:s} ({odds:d}:1)".format_map( vars(self) )
```

This uses the built-in `vars()` function to expose the attributes of an object as a simple dictionary that maps attribute names to values.

This is similar to using the `self.__dict__` internal dictionary.

The format string uses `:s` and `:d` as detailed specifications for the values to interpolate into the string. There’s a lot of flexbility in how numbers are formatted.

There’s another variation that can be handy.

```def __repr__( self ):
return "{class_:s}({name!r}, {odds!r})".format(
class_=type(self).__name__, **vars(self) )
```

This exposes the class name as well as the attribute values.

We’ve used the `!r` to request the internal representation for each attribute. For a string, it means it will be explicitly quoted.