Recreating Kotlin’s Built-In Delegates

For inspiration of what Delegated Properties to create, we’ll start by recreating the ones built into Kotlin, starting with Lazy.

Lazy

The Lazy Delegated Property allows you to initialize it with a function that takes no parameters and returns an object – preferably one that is relatively expensive to create. Then, the first time the property is retrieved, the object is created a stored, which subsequent lookups will use.

To start us off, let’s create an object that we can use as a flag to represent that the value isn’t initialized yet (we could use None, but it’s possible that the function could return None, and we’d start having some difficulties).

Note the optional read-only with the convenient readonly() class method. I’m not sure what else to say about it. If you have any trouble understanding any of these, ask me about it in the comments.

LateInit

Next up is LateInit. This isn’t quite as handy in Python as it is in Kotlin, since it is largely provided to allow for non-null properties in a class that implements the bean protocol. The problem is that the bean protocol requires a constructor that takes no arguments, where all the fields on the class are initialized to null. In Kotlin, you have null safety, and people like to mark their fields as non-nullable. So, in order to make the bean field non-nullable and yet have them start of null, Kotlin includes lateinit, which represents an uninitialized non-nullable field. If the field is accessed before it is ever set to something, then it triggers an exception. Technically, this isn’t a Delegated Property in Kotlin; it’s a language modifier, but it could be implemented as a Delegated Property.

What use does it have in Python? Well, it’s possible you might want to create a class where some or all of the attributes aren’t initialized right away, – rather they’re set later – but the class requires some or all of those attributes for certain actions. This way, instead of writing verifiers at the beginning of those actions to be certain that they were set, you simply access the attribute, and if it wasn’t set, it’ll raise an exception on its own.

The constructor and readonly() class method include the ability to initialize the property immediately, even though that’s not the typical case, but I like the idea of making it a little more flexible that way.

ByMap

Sometimes, you just want to take in a dictionary in the constructor and have attributes refer to that dictionary for their values. That’s what this Delegated Property does for you. So, it would be used like this:

class ByMapUser:
a = InstanceProperty(ByMap)
b = InstanceProperty(ByMap.readonly)
def __init__(self, dict_of_values):
# dict_of_values should have entries for 'a' and 'b', or else
# using either property could cause a KeyError, although setting
# before getting will add the entry to the dict if it wasn't
# already there
self.a.initialize(dict_of_values)
self.b.initialize(dict_of_values)

And here, a and b will read from and write to the provided dictionary instead of to the instance’s __dict__.

Here is where passing in name to get() and set() methods really comes in handy. Without the automatic calculating and passing of names, this would be more tedious to implement and use. The name would have to be taken in through the constructor, which means the user would have to provide it with initialize call. And that means that it would have to be updated by hand if they ever decide to change the name in a refactoring.

Observable

Kotlin also has the Observable Delegated Property, which I don’t see as being all that useful, so I won’t bother showing you that one.

Custom Delegated Property: Validate

One of the biggest reasons we ever need properties is to ensure that what is provided is a valid value to be given. While using the built-in Python property isn’t bad for this use case, it does still require the user to have to think about where to actually store the value, and it requires the verbosity of creating a method. But the biggest shortfall is when you need to reuse the logic of that property elsewhere. You could implement a full-blown descriptor for it, but that’s also excessive. With Validate, all you really need to implement is the validation function and possibly a helper function for creating the specific validator.

We had to unfortunately use lambda to implement the class methods. It could have been done by returning an inner function as well, but the lack of need for a name makes that excessive.

It also kind of sucks that we can’t do a proper error message in __init__() because we don’t have access to the name and instance. To get around this, I’m passing those arguments in as named arguments in initialize in the final version on GitHub (I wrote Validate for the first time after last week’s article and didn’t want to present any changes to last week’s code here). The Delegated Properties that don’t use them can just add **kwargs into their __init__() signature and ignore it.

Helpers

Shortening InstanceProperty

Always instantiating an InstanceProperty using that full name is tedious, and could potentially be enough to keep people from using it in the first place. So let’s make a shorter-named function that delegates to it:

def by(instantiator):
return InstanceProperty(instantiator)

I chose “by” as the name since it reflects the keyword used in Kotlin. You can use whatever name you want.

Why don’t I just rename InstanceProperty to something shorter? Because anything shorter would likely lose meaning, and we always want our names to have meaning.

Read Only Wrappers

I have a couple ideas for this, both of which aren’t compatible with 100% of Delegated Properties. Both of these make it so we don’t need to have a class method named “readonly”, and otherwise make implementing Delegated Properties easier.

Seeing that Delegated Properties aren’t required to have the readonly parameter, this isn’t 100% compatible. The respective properties should document whether or not they work with this function or not. This applies to the next one as well.

It’s annoying that we had to return a function within a function again, but when InstanceProperty needs a function for instantiating a Delegated Property, there’s not a whole lot of choice.

Outro

So that’s everything. Again, this is all in a GitHub repo, which turns out to be the same repo (descriptor-tools) I made for my book. The new code is added, but not all the tests and documentation are written for it yet. When that’s done, I’ll submit the rest to PyPI so you can easily install it with pip.

Thanks for reading, and I’ll see you next week with an article about the MVP pattern.