Skip to main content

The Prebound Method and Sentinel Object Pattern in Python

· 3 min read

Motivation

This article reflects on the blog posts, The Prebound Method Pattern and The Sentinel Object Pattern, by Brandon Rhodes. I'll briefly summarize the patterns and discuss my thoughts on them.

The Prebound Method Pattern

This pattern can be observed when using built-in functions such as random and logging. Instead of needing to create a new instance of the class, we can simply call the function directly. This is possible because a default instance is created within the module, and the instance method is assigned to the module's global namespace.

For example, a logger could be created as follows:

class Logger:
def __init__(self, name):
self.name = name

def log(self, message):
print(f"{self.name}: {message}")

_default_logger = Logger("default")
log = _default_logger.log

The log method is assigned to the module's global namespace, allowing it to be called directly. This is a simple example, but it's useful when you want to create and use a default instance of a class without needing to create a new one.

This supports the usage of the logger as follows:

import logger

logger.log("Hello World")

This pattern isn't super common, but it's a neat trick to know about. It feels a bit like the singleton pattern. However, since we don't restrict the number of instances that can be created, it's not truly a singleton.

The Sentinel Object Pattern

This pattern highlights that, despite Python's support for None, we can sometimes provide a more meaningful value to represent a missing value. This is particularly useful when we need to differentiate between a missing value and a valid one.

To illustrate with a similar example from the original article, consider the context of open-source software, where we might want to specify the type of license. We could use None to represent an unspecified license type. However, a valid alternative could be to assign it to a License object that clearly indicates the type of license (e.g., "not specified" or "unlicensed", which may mean different things).

To give a similar example provided in the original article, suppose in the context of open source software, we want to provide a value of the license type. We can use None to represent the case where the license type is not specified. However, a valid alternative could be assigning it to a License object that clearly indicates the type of license (whether it is "not specified" or "unlicensed", they may mean different things).

Here's an example of the pattern in action:

class License:
def __init__(self, name):
self.name = name

def get_name(self):
return self.name

class Package:
def __init__(self, name, license):
self.name = name
self.license = license

# INSTEAD OF
packages = [
Package("dummy1", None),
Package("dummy2", License("BSD")),
]

for package in packages:
if package.license is None:
print("not specified")
else:
print(package.license.get_name())

# WE CAN USE
UNLICENSED = License("unlicensed")
NOT_SPECIFIED = License("not specified")

packages = [
Package("dummy1", UNLICENSED),
Package("dummy2", NOT_SPECIFIED),
Package("dummy3", License("BSD")),
]

for package in packages:
print(package.license.get_name())

The advantage here is replacing None with more explicit values, which documents the intent more clearly. This may also reduce the need for None checks in the code.

Conclusion

The two patterns are subtle but can be quite useful in certain cases. It's good to be aware of them and use them when appropriate. The original articles are also worth reading for more detailed explanations.

Software Problems - Exceptions

· 3 min read

Exceptions are a common feature in popular languages like Python and Java. They serve to alter program execution under "exceptional" circumstances. However, handling them is not always straightforward. The concerns revolve around the following:

  • When an invoked function throws an exception, how should it be handled?
    • Should you catch and handle it?
    • Or let it bubble up to the caller? Does the caller's caller then need to worry about the exception? (a recursive question)

The problem is exacerbated because you can't simply avoid exceptions. Language libray functions often throw them (consider file handling, conversion, etc.). Moreover, writing exceptions is sometimes necessary, especially when dealing with external user input where "weird" and "unacceptable" cases may arise frequently. Furthermore, once you've written code that throws exceptions, you're likely to invoke that code yourself, necessitating handling your own exceptions. Poorly written code in this regard leaves no one to blame but ourselves.

The article Vexing exceptions by Eric Lippert is an interesting read on this topic. It classifies exceptions into four categories and suggests ways to handle (or not handle) them: fatal, boneheaded, vexing, and exogenous. A quick summary is provided in this post by Stephen Cleary. I'll briefly discuss what I learned from it.

The vexing exception is particularly interesting. Consider these two C# function signatures for parsing a string into an integer:

public static int Parse(string s)

public static bool TryParse(string s, out int result)

Invoking the first function, Parse, usually necessitates exception handling, as it will throw an exception if the input string is not convertible. An alternative approach when dealing with such functions is to seek or implement a variant like TryParse, which doesn't throw exceptions. TryParse returns a success indicator and the operation result. In cases of exceptions, it returns a failure indicator and a default value.

Here's an example in Python:

# Original
def parse_int(s):
return int(s)

# Usage
try:
result = parse_int("123")
print(result)
except ValueError:
print("Invalid input")

Using the TryParse variant will eliminate the need for exception handling, but it will require an if-else block to manage the success/failure case.

# TryParse
def try_parse_int(s):
try:
return True, int(s)
except ValueError:
return False, None

# Usage
success, result = try_parse_int("123")
if success:
print(result)
else:
print("Invalid input")

In summary, I think of vexing exceptions as "errors that are reasonably likely to occur". If so,

  • Use a "Try" variant without exceptions if available.
  • Implement a "Try" variant without exceptions if possible.
  • If neither is feasible, catch and handle (or re-raise) the exception.

Lastly, "Exogenous" exceptions, the siblings of vexing exceptions, are those thrown by code that you cannot reasonably control. A typical example is file handling functions. It's impractical to ascertain if a file exists before accessing it. Therefore, using code that does file handling likely requires try-catch blocks for possible exceptions. This differs from "Fatal" exceptions, where there's little you can do, while with exogenous exceptions, such as a file not being found, you can handle the situation, perhaps by creating a new file.

Here's a quick flowchart to summarize the ways to handle exceptions:

Summary of exception handling

End of Year 3 Sem 2

· 5 min read

CS3282 THEMATIC SYSTEMS PROJECT II

This is a continuation of the CS3281 OSS project, where we now take on the role of a senior developer, overseeing and guiding a new batch of students contributing to the project. In this project, we are presented with the option to either continue our work on the same project from CS3281 or venture into the realm of larger, external Open Source Software (OSS) projects. I must admit that I thoroughly enjoyed this module, as it provides the freedom to delve into what I am genuinely passionate about. For a more detailed account of my learning experiences, feel free to explore my write-up here. Now, let's delve into some high-level insights I gained from this journey.

Working on OSS: One aspect that struck me profoundly during this project was the dedication and commitment of independent developers who voluntarily invest their free time into contributing to and maintaining open-source software, all without monetary incentives. Witnessing this selfless dedication gave me a new appreciation for the sheer amount of work and effort OSS project maintainers put in. From triaging issues and discussing improvements to reviewing Pull Requests, they handle endless updates and upgrades. I can only imagine that for immensely popular projects, the workload may feel never-ending. However, this commitment has a flip side—the strong sense of community and the collective goal of creating high-quality software.

Being a Good Developer: Throughout the module, we participated in three lightning talks. Personally, presenting has never been my strongest suit, but these opportunities allowed me to practice and improve my public speaking skills.

100% will recommend.

CS4218 SOFTWARE TESTING

Getting into the depths of software testing made me realize that this aspect of software is not easy to manage at all. The goal of testing is to ensure that the software not only gets shipped but also works well. This involves a lot of attention to details like correctness, performance, etc. Just on validation and verification itself, there is a need for deep technical knowledge on how we model the software and what sort of testing we perform. While it doesn't always apply, it seems to me that it's a problem in the industry that we are not yet equipped with such knowledge and willingness to do so. I should say that even with the knowledge, testing itself seems to be difficult and not something that can be done without extra effort.

In this course, I learned a lot from the group project where we worked on implementing shell functions in Java and wrote tests for those functions. The arrangement was quite interesting in that we first implemented the functionality, then wrote the tests, and were also "forced" to practice TDD (Test-Driven Development) by writing code based on test cases that other teams wrote. There was also a "hackathon" where we spent time spotting bugs in other team's projects.

Overall, I think the stress didn't stem from the workload...but our team did work after midnight to finish a submission. At this moment, I have pretty much forgotten most of what I learned in this course, but I think it left me with a good impression of what software testing is about and how complex it can be.

I will recommend it.

CS4240 INTERACTION DESIGN FOR VIRTUAL AND AUGMENTED REALITY

I took this course to learn Unity and VR development. While the course itself does not teach you all the necessary details about AR/VR development, the professor provides high-level overviews and discusses concerns and considerations when developing such applications. The learning from this course is very much dependent on how hands-on you are. There are individual assignments where you can go all-out to complete the rather loosely defined deliverables, or you could do the minimum to meet the requirements. From my experience, there was a 3D game, an AR application, a group VR game, and a final group VR project. The requirements are not cast in stone, and the professor is quite flexible in terms of giving us the choice to make what we want for the final project. I enjoyed that it was very open-ended, and we could decide what we wanted to do. Overall, I think I gained some practical knowledge operating Unity and some basic ideas of how to implement a VR application.

CP4101 B.COMP.DISSERTATION (FINAL YEAR PROJECT)

This is my foray into research. To keep it short, I think research is not an easy job. The biggest difficulty I feel lies in the uncertainty that you are exploring. To construct a research plan that succinctly captures the core concept and the steps to achieve it is like telling a good story. You will need the ingredients, the preparation, and you may stay nervous and unsure all the time because you are not sure who will be in your audience. I'm simplifying a lot there, as the other aspect that I am still grappling with is that the devil is in the details. I think this is a mentally draining module, and I am really going into uncharted territories here. Even if I don't continue higher learning after my undergraduate days, I'm partially glad that I have this experience to understand what research is about.

GEH1045 World Religions

  • SU exercised
  • took the module to clear requirement
  • interesting spread of content covering different religions
  • improved my understanding of the context/origin of different religions
  • SU exercised
  • took the module to clear requirement
  • content and workload is manageable
  • essay writing skill is cruicial
  • I enjoyed watching the movie (a long long time ago 2) as part of the module