Basically metaclasses make the most sense when (a) you want to tinker with the fundamental lifecycle of objects without altering the mechanisms of the programming language itself, and (b) it would be inappropriate to have an implication of any subtyping relationship caused only by the desire to do (a) - and in fact there may already be an existing subtype hierarchy that you can't replace anyways. So overriding __new__ is a good case for that, and additional examples of it would be if you want to institute a singleton or multiton pattern for instance creation without forcing everybody to deal with factory-method boilerplate calls everywhere. That's what the Path() vs PosixPath() example effectively was; while I doubt pathlib does this you could conceptually use a metaclass there to implement a multiton over the space of strings supplied as paths (which no doubt sounds weird... until you start to think about adding in functionality for Python-aware locking of directories or files). I don't think there is really any conflict between what inheritance is good for vs when to use metaclasses; really it just boils down to "gnarly details that have nothing to do with type relationships but do make sense to wire into multiple unrelated classes without breaking existing logic that was never metaclass logic in the first place". If you want more reading material on the topic, refer to Kizales "The Art of the Metaobject Protocol". His work is the earliest significant material I recall being published on the topic, and should be a good place to hunt for papers or books that have him as a reference source.
4:10 - That's not quite correct. What that actually does is creating a class attribute, not an instance attribute. See here: >>> Foo = type('Foo', (), {'x': 123}) >>> Foo.x 123
@@BboyKeny There's really nothing static about Python. It's all pretty dynamic. That said, yes, other languages call that a static attribute/property/member. In Python pretty much everything is an object and can be referenced by a variable or attribute. Methods are just functions and are referenced through a name in the class dict: >>> class Foo: ... ... >>> def baz(self, x): ... return x * 2 ... >>> Foo.bar = baz >>> f = Foo() >>> f.bar(123) 246
@@BboyKeny the part that won't be obvious is that attributes defined on the object (e.g. "def __init()__: self.x = 7") get cached in a different dict than attributes defined on the class ("eg. class Foo: x = 9"). It looks like they are the same attribute, and they almost are the same. The latter provides a default for all instances, but instances can effectively shadow that and have their own value. The two attributes are different entities, but if you do something like "f = Foo() print(f.x)" then the print statement will work in both cases and you have no idea which source of "x" is being used. You can look at Foo.__dict__ and f.__dict__ to see where "x" is and which value it has in either location.
So you're saying I can connect to a database in a metaclass and whenever I define a class which needs to interact with the database it just uses this metaclass. Sounds nice! But probably not something durable for the long term
I don't know enough about Django to be able to answer that, but this might help: medium.com/swlh/how-django-use-data-descriptors-metaclasses-for-data-modelling-14b307280fce
Basically metaclasses make the most sense when (a) you want to tinker with the fundamental lifecycle of objects without altering the mechanisms of the programming language itself, and (b) it would be inappropriate to have an implication of any subtyping relationship caused only by the desire to do (a) - and in fact there may already be an existing subtype hierarchy that you can't replace anyways. So overriding __new__ is a good case for that, and additional examples of it would be if you want to institute a singleton or multiton pattern for instance creation without forcing everybody to deal with factory-method boilerplate calls everywhere. That's what the Path() vs PosixPath() example effectively was; while I doubt pathlib does this you could conceptually use a metaclass there to implement a multiton over the space of strings supplied as paths (which no doubt sounds weird... until you start to think about adding in functionality for Python-aware locking of directories or files). I don't think there is really any conflict between what inheritance is good for vs when to use metaclasses; really it just boils down to "gnarly details that have nothing to do with type relationships but do make sense to wire into multiple unrelated classes without breaking existing logic that was never metaclass logic in the first place". If you want more reading material on the topic, refer to Kizales "The Art of the Metaobject Protocol". His work is the earliest significant material I recall being published on the topic, and should be a good place to hunt for papers or books that have him as a reference source.
4:10 - That's not quite correct. What that actually does is creating a class attribute, not an instance attribute. See here:
>>> Foo = type('Foo', (), {'x': 123})
>>> Foo.x
123
Good spot.
So like a static attribute/property?
@@BboyKeny There's really nothing static about Python. It's all pretty dynamic. That said, yes, other languages call that a static attribute/property/member.
In Python pretty much everything is an object and can be referenced by a variable or attribute. Methods are just functions and are referenced through a name in the class dict:
>>> class Foo: ...
...
>>> def baz(self, x):
... return x * 2
...
>>> Foo.bar = baz
>>> f = Foo()
>>> f.bar(123)
246
@@BboyKeny the part that won't be obvious is that attributes defined on the object (e.g. "def __init()__:
self.x = 7") get cached in a different dict than attributes defined on the class ("eg. class Foo:
x = 9"). It looks like they are the same attribute, and they almost are the same. The latter provides a default for all instances, but instances can effectively shadow that and have their own value. The two attributes are different entities, but if you do something like "f = Foo()
print(f.x)" then the print statement will work in both cases and you have no idea which source of "x" is being used. You can look at Foo.__dict__ and f.__dict__ to see where "x" is and which value it has in either location.
So you're saying I can connect to a database in a metaclass and whenever I define a class which needs to interact with the database it just uses this metaclass. Sounds nice! But probably not something durable for the long term
Is there a reason why meta classes are so much used in the django framework?
I don't know enough about Django to be able to answer that, but this might help: medium.com/swlh/how-django-use-data-descriptors-metaclasses-for-data-modelling-14b307280fce
Using type to create classes would be useful if you wanted to generate your classes dynamically. Polymorphic mutating code, anyone? :)