Python get class but exclude inherited attributes and methods - python

Hi I have 2 classes a Model and Parent Class
ModelMaster:
myvar: string = "one"
def myfun():
# do stuff
Model(ModelMaster):
first: string = "hello"
second: int = 123
Modelmaster contains functions that all models should use and attributes
When using the Model as purely a model I want to get the Model instance including all of its attributes but I dont want to see anything that was inherited from ModelMaster
Is there someway to get, from instance of Model, a copy of that model with just the attributes of that class that has all parent attributes and methods removed.
It seems like there would be something built into python3 to do this, rather than writing this myself
anyone have any ideas?
thanks,
Simon

You can create a new class with Model.__dict__ passed to the type constructor, but not Model's base class:
PureModel = type('PureModel', (), dict(Model.__dict__))
so that:
print(PureModel().first)
outputs:
hello
and that PureModel().myvar would result in:
AttributeError: 'PureModel' object has no attribute 'myvar'

The best solution I found was to use
annotations = self.__annotations__
keys = {}
for annotation in annotations:
name = annotation
type = annotations[annotation]
data = self.__getattribute__(annotation)
keys.append({"name":name, "type":type, "data":data})
this method can exist in any class in the inheritance tree and it will only return the attributes for the top level class
I'm still hoping that someone might come up with a cleaner version but this works for me

Related

Extending parent/children classes in python

Is it possible in python for a nested class to extend its parent?
Like this:
class Parent:
class Child(Parent):
pass
child = Parent.Child()
Is it possible to do this in the opposite direction?
Like this:
class Parent(Child):
class Child:
pass
parent = Parent()
From what I know this is not possible, even with from __future__ import annotations.
The best known way around this is just not to make nested classes.
Important:
The purpose of this question is to make it clear if this is even possible in the python language.
There is no "final goal", objectives to be accomplished with this approach or justification for it.
Don't spam in the comments/answers about "how bad this code is".
No and Yes.
No, because when you inherit from a class, that class must be defined before you can inherit from it. Neither of your code examples will work due to this.
Yes, because Python is a dynamic language and you can change (monkey-patch) the base classes even after defining them:
class Temp:
pass
# example 1
class Parent:
class Child(Temp):
pass
Parent.Child.__bases__ = (Parent,)
# example 2
class Parent(Temp):
class Child:
pass
Parent.__bases__ = (Parent.Child,)
Why use the Temp class?
Classes automatically inherit from object. Due to a bug (https://bugs.python.org/issue672115), we cannot change __bases__ if a class inherits from object. Hence, we inherit from a temporary (Temp) class to avoid that issue.

Share data between base class and Meta class

I have the following django model:
class Article(models.Model):
filename = models.CharField(max_length=255)
collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
keys = ['filename', 'collection']
class Meta:
constraints = [
models.UniqueConstraint(
fields=['filename', 'collection'],
name='article_key')
]
As you can see I've defined the same list ['filename', 'collection'] in both the base class and the Meta class. I would like to define it once. I can't define it in Meta because then I get 'Meta got an unrecognised attribute 'keys'. So I must define it in the base class and access it from Meta. I don't know how to share data between the two. I've tried doing:
self.keys
in Meta but that gives 'self is not defined'. I've also tried with just 'keys' but that's also not defined. Any tips? Thanks.
EDIT
Thank you to Willem for pointing out that I can define keys in Meta if I just call it '_keys'. If I do this, however, the question is then how do I access _keys from the base class? I've tried 'meta._keys' and 'Meta._keys'. Both not defined.
EDIT 2
For clarity, the reason that I want 'keys' defined in the base class is that I will (a) be accessing it from properties on the base class, and (b) want to be able to access it from the outside.
You can declare it before the class, then reference it from both the model class and it' Meta:
# making it a tuple since you probably don't want
# it to be mutable
_ARTICLE_KEYS = ('filename', 'collection')
class Article(models.Model):
# making it an implementation attribute since you
# probably don't want to be writeable
# (hint: provide a read-only property for access)
_keys = _ARTICLE_KEYS
class Meta:
constraints = [
models.UniqueConstraint(
fields=_ARTICLE_KEYS,
name='article_key')
]
But this is still ugly IMHO and very probably unecessary - the model's methods should be able to access those values thru self._meta.contraints[0].fields or something similar (don't have models with such constraints at hand right now so I can check how this is actually transformed by the models's metaclass but inspecting self._meta in your django shell should give you the answer).
The methods of a nested class cannot directly access the instance attributes of the outer class.
So, in your case, If you won't use the keys list in the Article class, just defined it once in the Meta class. Otherwise, you need to defined twice!

Can python class variable be used as both class and instance variable? [duplicate]

I'm simultaneously learning Python while picking up Django. I'm familiar with many other languages.
In the following code snippet, x is a class variable of class Foo.
class Foo(object):
x = 9000
Given the previous declaration, the following works fine.
print Foo.x
The Django framework lets you create your model by defining Python classes. It makes fields out of the different class variables in your Python classes.
class Question(models.Model):
question_text = models.CharField(max_length=200)
Why does the following code snippet:
#!/usr/bin/env
import os, django
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'
django.setup()
from polls.models import Question, Choice
print Question.question_text
throw the following error:
AttributeError: type object 'Question' has no attribute 'question_text'
As far as I'm understanding everything my Question class has a single static member defined: Question.question_text.
Django models use a metaclass to alter what is normal class behaviour.
Use dir(Question) and you'll see there are different attributes on that class now. This is custom behaviour just for Django models however.
If you are curious you can study the metaclass __new__ method, but it does a lot of work specific to Object Relational Mapping tasks.
Magic.
No, really.
Python classes aren't set-in-stone structure, like they are in C++. They are, themselves, just objects — instances of another type:
class Foo(object):
pass
print(type(Foo)) # <class 'type'>
You can even make a class like you'd make any other object, by calling type. This:
class Bar(object):
a = 1
b = 2
Is really (more or less) syntactic sugar for this:
Bar = type('Bar', (object,), {'a': 1, 'b': 2})
type takes the name of your new class, a list of its superclasses, and a dict of all the attributes of the class, and spits out a new class.
But, because type is just a class like any other, it's possible to subclass it and give it different behavior. And this is what Django has done: it's created a subclass of type that does something different with the dict of attributes you pass to it.
You don't see this happening directly in your own code, but if you check type(models.Model), you'll find out its type is not type, but something specific to Django. It probably has "meta" in the name, because it's called a metaclass: the class of a class.
This is a fairly common pattern for making "declarative" libraries in Python, where the attributes of a class actually define some kind of structure. You can see the same thing in form validation (wtforms), schema validation (colander), other ORMs (sqlalchemy), and even the stdlib enum module.
Question is an object of type type. You want an instance of Question:
>>> q= Question(text = "Does a dog have the buddha nature?")
Then you should get
q.text
"Does a dog have the buddha nature?"
Note that this object will not persist unless you save() it:
>>> q.save()

Django, Python, and Class Variables

I'm simultaneously learning Python while picking up Django. I'm familiar with many other languages.
In the following code snippet, x is a class variable of class Foo.
class Foo(object):
x = 9000
Given the previous declaration, the following works fine.
print Foo.x
The Django framework lets you create your model by defining Python classes. It makes fields out of the different class variables in your Python classes.
class Question(models.Model):
question_text = models.CharField(max_length=200)
Why does the following code snippet:
#!/usr/bin/env
import os, django
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'
django.setup()
from polls.models import Question, Choice
print Question.question_text
throw the following error:
AttributeError: type object 'Question' has no attribute 'question_text'
As far as I'm understanding everything my Question class has a single static member defined: Question.question_text.
Django models use a metaclass to alter what is normal class behaviour.
Use dir(Question) and you'll see there are different attributes on that class now. This is custom behaviour just for Django models however.
If you are curious you can study the metaclass __new__ method, but it does a lot of work specific to Object Relational Mapping tasks.
Magic.
No, really.
Python classes aren't set-in-stone structure, like they are in C++. They are, themselves, just objects — instances of another type:
class Foo(object):
pass
print(type(Foo)) # <class 'type'>
You can even make a class like you'd make any other object, by calling type. This:
class Bar(object):
a = 1
b = 2
Is really (more or less) syntactic sugar for this:
Bar = type('Bar', (object,), {'a': 1, 'b': 2})
type takes the name of your new class, a list of its superclasses, and a dict of all the attributes of the class, and spits out a new class.
But, because type is just a class like any other, it's possible to subclass it and give it different behavior. And this is what Django has done: it's created a subclass of type that does something different with the dict of attributes you pass to it.
You don't see this happening directly in your own code, but if you check type(models.Model), you'll find out its type is not type, but something specific to Django. It probably has "meta" in the name, because it's called a metaclass: the class of a class.
This is a fairly common pattern for making "declarative" libraries in Python, where the attributes of a class actually define some kind of structure. You can see the same thing in form validation (wtforms), schema validation (colander), other ORMs (sqlalchemy), and even the stdlib enum module.
Question is an object of type type. You want an instance of Question:
>>> q= Question(text = "Does a dog have the buddha nature?")
Then you should get
q.text
"Does a dog have the buddha nature?"
Note that this object will not persist unless you save() it:
>>> q.save()

Why can't I access class Meta as a attribute of Django Model Class?

In Python I can define:
class Person(object):
name = "Easwar"
age = 35
sex = "male"
class Occupation:
name = "my_job"
I can then access it
>> p = Person()
>> p.Occupation.name
>> # prints "my_job"
However in Django, if I have a model defined with Class Meta inside I cannot do this
>>> m = SomeDjangoModel()
>>> m.Meta
>>> # prints AttributeError!
Why is this ?
How is Django's inner Meta class different from a regular Python class ?
I did research this and didnt come up with anything similar asked here.
Please excuse me if I have missed it.
Thanks in advance for the help.
The Meta attribute is changed by metaclass.
Try:
SomeDjangoModel._meta
A Meta attribute defined on a class is a bespoke pattern for associating and containing additional data that is going to be introspected by the class' metaclass upon type creation.
You can review the ModelBase metaclass and see how it uses the Meta attribute to configure the resulting model class that's being constructed.

Categories