Thursday 30 June 2022

Create Child Instance from Parent Instance

Recently I was thinking for a while about how better design this case (in python). I have a Base class and a Child class and I want to create a Child instance from an existing Base instance. I ended up with this design


import json

class City:
    def __init__(self, name, population):
        self.name = name
        self.population = population
        self.neighbourhoods = []

    def add_neighbourhood(self, name):
        self.neighbourhoods.append(name)

class FrenchCity(City):
    def __init__(self, name, population, urban_area_population):
        super().__init__(name, population)
        self._initialize(urban_area_population)

    def _initialize(self, urban_area_population):
        self.urban_area_population = urban_area_population
        self.related_cities = []

    def add_related_city(self, city):
        self.related_cities.append(city)

    @classmethod
    def create_from_city(cls, city, urban_area_population):
        # I'm doing object.__new__ rather than calling FrenchCity constructor cause in other classes 
        french_city = object.__new__(cls)
        french_city.__dict__.update(city.__dict__)
        french_city._initialize(urban_area_population)
        return french_city


city = City("Paris", 2000000)
city.add_neighbourhood("Belleville")
f_city = FrenchCity.create_from_city(city, 12000000)
f_city.add_related_city("Saint Mande")
print(json.dumps(f_city, indent=4, default=lambda x: x.__dict__))

So as you can see I have added a create_from_[parent] static method to the child class. In it I assign the data in the parent instance __dict__ to the child instance __dict__. Yes, I know that direct access to __dict__ should be avoided as it's mainly an implementation detail, but at the same time it's well known and I don't think it's going to change in the near future.

The other interesting part in the create method is that rather than invoking the child constructor, I'm calling object.__new__ (that's also why I've split the initialization functionality between the normal __init__ method and an _initialize method) rather than the normal Child "constructor" (that ends up calling both __new__ and __init__). In this particular class I could use that "constructor" cause it's clear what parameters to pass from the parent instance fields, but this is not always the case, so the generic way is to create an instance of the child class using __new__, then initialize the child properties with an _initialize method (that we also use when creating the instance from scratch rather than from an existing parent instance), and then set the properties from the parent instance using __dict__

There's a gotcha. We could have a class that were using __slots__ rather than __dict__. Adapting to that should not be a big deal, but I would have to review how __slots__ work.

Saturday 18 June 2022

Measuring Time

In some recent script at some point I wanted to measure the time consumed in different sections of code (in a basic manner, it's not profiling information to store somewhere or anything, just to print on screen and probably end up removing it). I ended up with this:


start = time.time()
# do things
print(time.time()-start)
start = time.time()
# do something
print(time.time()-start)
start = time.time()
# do something
print(time.time()-start)
start = time.time()

Those 2 lines for each time I want to print and start the next "snapshot" add quite a bit of noise to the code. Oddly, I did not find an immediate, ready-made solution for this. so I came up with a nice use for a generator. We can have a generator function like this:


def elapsed_generator_fn():
    start = time.time()
    yield # we need a first next() call to perform this initialization
    while True:
        now = time.time()
        elapsed = now - start
        start = now
        yield elapsed

And write our code this way:


    elapsed = elapsed_generator_fn()
    # init it
    next(elapsed)

	# do something
    print(f"elapsed: {next(elapsed)}")

	# do something
    print(f"elapsed: {next(elapsed)}")

	# do something
    print(f"elapsed: {next(elapsed)}")


In the above code the first next() call is for initialization (setting the initial start value). It looks a bit odd in the code, so we could fix it by adding a function that creates the generator-iterator, does this init call, and returns it.


def elapsed_generator_factory():
    generator = elapsed_generator_fn()
    next(generator)
    return generator
	
elapsed = elapsed_generator_factory()
# do something
print(f"elapsed: {next(elapsed)}")

# do something
print(f"elapsed: {next(elapsed)}")

# do something
print(f"elapsed: {next(elapsed)}")

As I've said, it seems to me like a cool use for generators. If we want something more complete (that gives us the total time, that stores the different snapshots...) we should move on and end up with a simple class like this.


class Elapsed:
    def __init__(self):
        self.initial_start = None
        self.snapshots = {}

    def start(self):
        self.initial_start = time.time()
        self.start = self.initial_start

    def take_snapshot(self, name):
        now = time.time()
        self.snapshots[name] = now - self.start
        self.start = now
        return self.snapshots[name]
    
    def total(self):
        return time.time() - self.initial_start

    def retrieve_snapshots(self):
        return self.snapshots
		
    elapsed = Elapsed()
    elapsed.start()
    time.sleep(2)
    print(f"elapsed: {elapsed.take_snapshot('first')}")

    time.sleep(1)
    print(f"elapsed: {elapsed.take_snapshot('second')}")

    time.sleep(3)
    print(f"elapsed: {elapsed.take_snapshot('third')}")

    print(f"total elapsed: {elapsed.total()}")

    print(f"snapshots: {elapsed.retrieve_snapshots()}")


Friday 3 June 2022

Python Type Hints and DI

After some initial reluctance I've started to use Python type hints for any non trivial code. I'm not strict at all (usually I don't declare all types, only when they are meaningful to me), and most times I don't enable the type checker (in vscode-pylance), I just want the sort of documentation that type declarations provide, and the improved intellisense experience. Somehow type hints in python seem much better to me than using typescript over javascript.

I've found something really interesting. We know that type hints are not enforced by the Python runtime (so Python remains as dynamic as always), they are intended to be used by third party tools before you run your code. This said, the interesting discovery to me is that the type information is available at runtime in form of annotations. So while the runtime won't impose any resctrictions based on these annotations, your code can use this information. In python 3.10 it's available through inspect.get_annotations(). I guess this is how the diffeent Dependency Injection libraries that have flourished in recentyears work.

This reminds me of how Dependency Injection works in Angular with TypeScript. So far, In JavaScript we don't have a standard way (like annotations) to add information (metadata) to our objects, but there is a proposal, the reflect-metadata api, and angular-polyfill.js takes care of adding it to our browser. If you add to your tsconfig.json file the emitDecoratorMetadata option (that also needs the experimentalDecorators option), TypeScript will save type information as metadata in the generated JavaScript (it generates code that adds this information to Reflect.metadata if the API exists). Example of generated JavaScript code:


var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);


__decorate([
    deco,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", void 0)


As explained here TypeScript will only do this for classes, functions... that have been decorated (with any decorator, you can just create a dummy one). You have a pretty nice explanation of how DI works in Angular-TypeScript here.