What is #property in Django?
Here is how I understand it: #property is a decorator for methods in a class that gets the value in the method.
But, as I understand it, I can just call the method like normal and it will get it. So I am not sure what exactly it does.
Example from the docs:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
elif self.birth_date < datetime.date(1965, 1, 1):
return "Baby boomer"
else:
return "Post-boomer"
#property
def full_name(self):
"Returns the person's full name."
return '%s %s' % (self.first_name, self.last_name)
What is the difference of if it is there vs if it isn't?
As you see, the function full_name returns a string with the persons first and last name.
What the #property decorator does, is declare that it can be accessed like it's a regular property.
This means you can call full_name as if it were a member variable instead of a function, so like this:
name = person.full_name
instead of
name = person.full_name()
You could also define a setter method like this:
#full_name.setter
def full_name(self, value):
names = value.split(' ')
self.first_name = names[0]
self.last_name = names[1]
Using this method, you can set a persons full name like this:
person.full_name = 'John Doe'
instead of
person.set_full_name('John Doe')
P.S. the setter above is just an example, as it only works for names that consist of two words separated by a whitespace. In real life, you'd use a more robust function.
In some languages users are encouraged to make attributes private and create public getter and setter methods, e.g. in some made up Python-like language with private and public:
class Foo:
private bar
public get_bar(bar):
return self.bar # or look it up in a database
# or compute it on the fly from other values
# or anything else
public set_bar(new_bar):
self.bar = new_bar
The argument is about providing a stable interface. If you want to change the inner workings of your class, e.g. to look it up from a database or compute it, users of the class won't have to change anything; they just keep calling the getter and setter.
In Python we don't really have private attributes, and we want simple syntax. So we flip it: programmers often directly access an object's attributes. But what if you want to change the internal behaviour? We don't want to change the class' interface.
#property lets you change how bar works internally without changing the external interface. Users of the class can still access foo.bar, but your internal logic can be completely different:
class Foo:
def __init__(self, bar):
self.bar = bar
def main():
f = Foo()
print(f.bar)
# Later we can change to something like this without breaking other code
class Foo:
def __init__(self, bar):
self.save_bar_to_database(bar) # Or anything else
#property
def bar(self):
return self.load_bar_from_database()
It is a simple way, where you can get variables in the table and provide another variable which can be used directly as it was a variable.
#property
def total(self):
total_price = self.products.price_each * self.quantity_prt
return total_price
such as abouve function you can get number of products and price and ue a property and make a variable of total price.
Related
I tried to create a data structure in python, where I have an outerClass, innerClass, both would include several variables and the outerClass has a list, storing objects of innerClass instances.
I managed to create the outerClass instances but failed to do it with innerClass instances, especially unable to append them to my innerInstancesList.
I'm quite new to python, not sure exactly if this is the best way implement this structure.
Trying to make something similar:
Outerinstance1
variable1
variable2
Innerinstance1
variable1
variable2
Innerinstance2
variable1
variable2
Innerinstance3
variable1
Outerinstance2
variable1
Is there a better way to implement this in python? My sample code:
class outerClass:
def __init__(self, name):
self.name = name
self.innerInstancesList= []
self.innerClass = self.innerClass()
class innerClass:
def __init__(self):
self.id = 0
self.c = "char"
def init2(self, id, c):
self.id = id
self.c = c
outerInstance = outerClass("Outerinstance1")
print (hex(id(outerInstance)))
for a in range(0,5):
outerInstance.innerClass.init2(1, a)
x = outerInstance.innerClass
print (hex(id(x)))
outerInstance.innerInstancesList.append(x)
It appears that ultimately, you want instances of one class to each track multiple instances of another class. This doesn't actually require that one class be defined inside the other. Let's call them Group and Member; each Group instance can hold multiple instances of Member. A straightforward way to declare and demonstrate such a relationship would look like so:
class Group:
def __init__(self, name):
self.name = name
self.members = []
def __repr__(self):
return f'Group(name={self.name}, members={self.members})'
class Member:
def __init__(self, id, char):
self.id = id
self.char = char
def __repr__(self):
return f'Member(id={self.id}, char={self.char})'
group = Group('Test Group')
print(group)
for a in range(5):
member = Member(a, chr(a + 97))
print(member)
group.members.append(member)
print(group)
I've added __repr__ methods (using f-strings) so that it's easy to tell what objects are which when printed. As you can see, both classes are defined at the top (module) level, then multiple instances of Member are appended to the members list of the Group instance.
This outputs:
Group(name='Test Group', members=[])
Member(id=0, c='a')
Member(id=1, c='b')
Member(id=2, c='c')
Member(id=3, c='d')
Member(id=4, c='e')
Group(name='Test Group', members=[Member(id=0, c='a'), Member(id=1, c='b'), Member(id=2, c='c'), Member(id=3, c='d'), Member(id=4, c='e')])
You could avoid some boilerplate in your class definitions (at least in this simple example) by using dataclasses. They're a handy way to simplify declarations of classes that mostly just store some data fields like this, because they can automatically generate __init__ and __repr__ methods for you, among other things.
from dataclasses import dataclass, field
#dataclass
class Group:
name: str
members: list = field(init=False, default_factory=list)
#dataclass
class Member:
id: int
char: str
# Rest of the code unchanged; will generate the same output
I don't think there is. I think you might be better without inner classes, does not seem to be a great reason to use one. You could just add the class to the constructor, append it, and list it that way.
I'm not quite sure how to ask this question, let alone find the answer, partially because I may be completely wrong in my approach to solving this problem.
I'm writing some Python, and I have a class (Users) which is basically used to instantiate a number of objects of a particular type (User), and then provide a number of methods to help me work with those objects in a more straightforward manner. The code I have looks like this:
from defusedxml.ElementTree import parse
class Users:
def __init__(self, path):
self.path = path
self.users = []
users = parse(path).getroot()
for user in users:
u = User.user_from_xml(user)
self.users.append(u)
def __iter__(self):
self.i = 0
return self
def __next__(self):
if self.i < len(self.users):
self.i += 1
return self.users[(self.i - 1)]
else:
raise StopIteration
def get_user_by_id(self, user_id):
return next((user for user in self.users if user.id == user_id), None)
def search_attribute(self, attribute, value):
return [user for user in self.users if
getattr(user, attribute, None) != None and
value.lower() in str(getattr(user, attribute).lower())]
class User:
def __init__(self, user_id, username, email, first_name, last_name):
self.id = int(user_id)
self.username = username
self.email = email
self.first_name = first_name
self.last_name = last_name
def __repr__(self):
if self.first_name == None or self.last_name == None:
return "%s (User Id: %s)" % (self.username, self.id)
return "%s %s (%s)" % (self.first_name, self.last_name, self.username)
#staticmethod
def user_from_xml(user):
return User(
user.get("id"),
element.find("username").text,
element.find("email").text,
element.find("firstname").text,
element.find("lastname").text
)
I have a number of other objects stored in XML in a similar way - for example, Events. I can see the need to use the same methods defined in Users, with the only real difference being the type of object contained in the list created in __init__.
So the question is: what's the best way for me to make this code reuseable, while maintaining readability, etc.? Or maybe I'm on completely the wrong track.
If these class methods will truly be identical, I think the simplest method would be to just make a more generic class to replace Users that takes another class (e.g., User or Event) as an argument in its __init__ method. Your class might look like so:
class Things(object):
def __init__(self, PATH, Thing): #Thing is a class
self.PATH = PATH
self.users = []
users = parse(PATH).getroot()
for thing in things:
t = Thing.thing_from_xml(thing)
self.things.append(t)
def methods...
A more robust/scalable solution might be to use inheritance.
You could create an abstract base class that has all of your methods, and then override the base class's __init__ method within each child class. I'll draw out an example of this:
class AbstractBaseClass(object):
def __init__(self, PATH):
self.PATH = PATH
self.things = []
def methods...
class Users(AbstractBaseClass):
def __init__(self, PATH):
super(Users, self).__init__() # calls the parent __init__ method
users = parse(PATH).getroot()
for user in users:
u = User.user_from_xml(user)
self.things.append(u)
#no need to define methods, as they were already defined in parent class
#but you can override methods or add new ones if you want
Your Events class would also inherit AbstractBaseClass and thereby have all of the same methods of Users. You should read up on inheritance, it's a great tool.
EDIT TO ADDRESS YOUR COMMENT:
Properties might be a good way to get that attribute users back into to your Users class. Change things to _things to suggest that it is private, and then create a users property, like so:
class Users(AbstractBaseClass):
#property
def users(self):
return self._things
This way you can call Users.users and get Users._things.
If you really, really care about code reuse, you could even do something dynamic like this in __init__:
class AbstractBaseClass(object):
def __init__(self, PATH):
self._things = []
self.PATH = PATH
setattr(self, self.__class__.__name__.lower(), self._things)
#This creates an attribute that is the lowercase version of the
#class name and assigns self._things to it
Note: I think this is a little ugly and unnecessary. Also, since you would have two attributes that are the same thing - it might lead to your object being in an incoherent state.
That said, to me Users.users seems redundant. I'm not fully aware of the context of your problem but I think I would prefer to have my Users objects simply behave like the list users, but with extra methods (those you defined).
In AbstractBaseClass you could define __iter__ to be the __iter__ of the _things attribute.
class AbstractBaseClass(object):
def __init__(self, PATH):
self._things = []
self.PATH = PATH
def __iter__(self):
return self._things.__iter__()
#You might also want this - it lets you do list-like indexing
def __getitem__(self, i):
return self._things.__getitem__(i)
I think the above does essentially what you were doing with __iter__ and __next__ in your original code, but in a cleaner way. This way, you don't have to access _things or users directly to play with a list of your user objects; you can play with a list of users through your Users class, which, by its name, seems like the purpose of the class.
For a program that creates a timetable for a doctor(specialist) I want to use certain attributes of an object created by a different class to be used in the class that makes the timetable for the doctor.
class makePatient(object):
def __init__(self,name,room):
self.name = name
self.room = room
def getPatient(self):
print(self.name)
print(self.room)
class makeSpecialist(object):
def __init__(self,name,specialization,timetable):
self.name = name
self.specialization = specialization
self.timetable = timetable
def getSpecialist(self):
print(self.name)
print(self.specialization)
print(self.timetable)
class makeAgenda(object):
def addAgenda(self):
self.timetable.append()
#I want to append the name of the patient I have defined here.
print(self.timetable)
patient1 = makePatient("Michael","101")
specialist1 = makeSpecialist("Dr. John","Hematology",[])
What do I do now, to make sure that the name "Michael" gets added to the list [] of specialist Dr. John?
Thanks in advance, I will provide further details if necessary!
I think another approach would be better; you can put the whole makePatient object into the timetable for the specialist:
specialist1 = makeSpecialist("Dr. John", "Hematology", [patient1])
Now you can access the names and other attributes of the patients in a specialist's timetable:
for patient in specialist1.timetable:
print(patient.name)
You can also define a __repr__ method to tell Python how to display an object, rather than the current getPatient:
class makePatient(object):
# ...
def __repr__(self):
return "{0} (room {1})".format(self.name, self.room)
Now when you print the whole timetable:
>>> print(specialist1.timetable)
You get the necessary information:
[Michael (room 101)]
Note also that the classes should probably be called, simply, Patient, Specialist and Agenda; the make is implied.
Finally, you will get errors in makeAgenda.addAgenda as, without an __init__, self.timetable doesn't exist for a makeAgenda object, and an empty append() doesn't do anything anyway.
Classes are often used to represent entities and operations allowed on them, include constructing, or making, new instances of them. Therefore, your classes would be better named simplyPatient, Specialist, andAgenda. The name of the method that constructs a new instance of any class in Python is always__init__().
That said, after creating aPatientand aSpecialistyou could then add patient instances to the specialist's timetable/agenda by passing it to aSpecialistmethod specifically designed for that purpose. In other words, a Specialist "has-a" Agenda instance namedtimetableand to which patients can be added via an appropriately namedadd_to_timetable()method.
Here's what I mean -- note I've modified your code to follow PEP 8 -- Style Guide for Python Code guidelines which I also suggest that you follow:
class Agenda(object):
def __init__(self):
self.events = []
def append(self, event):
self.events.append(event)
class Patient(object):
def __init__(self, name, room):
self.name = name
self.room = room
def get_patient(self):
print(self.name)
print(self.room)
class Specialist(object):
def __init__(self, name, specialization):
self.name = name
self.specialization = specialization
self.timetable = Agenda()
def add_to_timetable(self, patient):
self.timetable.append(patient)
def get_specialist(self):
print(self.name)
print(self.specialization)
print(self.timetable)
specialist1 = Specialist("Dr. John", "Hematology")
patient1 = Patient("Michael", "101")
specialist1.add_to_timetable(patient1)
I'm not too sure what you're trying to accomplish here with method that just print values or with the makeAgenda class, but here's how you can get Michael in Dr. John's list:
specialist1.timetable.append(patient1.name)
If I am creating a class below, can someone please explain the proper way to create the instance and also pass in the arguments. I though that I would be able to pass in the initial arguments at time of initiation but cannot seem to get this to work. Below is an example of the class:
Class Course(object):
"""
Represents a University course
"""
def _init_(self, name, teacher, description):
self.name = name
self.teacher= price
self.description = description
def _str_(self):
return "{name} ({desc}): {teacher:}".format(name=self.name,
desc=self.description, teacher=self.teacher)
So in my program I'd like to create an instance which I though I do by using something like class = Course().
But isn't there a way to initiate the 3 variables at the same time? Something along the lines of class('Math101', 'Dr. Know Nothing', 'Learning is Fun') ?
Then I can just print class and get my desired output string as defined from within the class? I might be missing an import somewhere which is also confusing to me if I need to import modules or with a class all I need to do is the initial class = Course() ?
You have to declare special methods with double underscores: __init__, not _init_. And then, when creating an object, you have to pass the arguments like: course1 = Course(...parameters...):
class Course(object):
"""
Represents a University course
"""
def __init__(self, name, teacher, description):
self.name = name
self.teacher = teacher
self.description = description
def __str__(self):
return "{name} ({desc}): {teacher:}".format(name = self.name,
desc = self.description, teacher = self.teacher)
course1 = Course('Math101', 'Dr. Know Nothing', 'Learning is Fun')
print course1
Output:
Math101 (Learning is Fun): Dr. Know Nothing
Notes:
The Python keyword to create a class is class, not Class. Python is case-sensitive for keywords.
You were assigning price to self.teacher, which would lead in an error because price is not declared anywhere. I think it's just a typo. You may use self.teacher = teacher instead.
You must not use Python keywords (reserved names) as name of variables, because if you did, you would be hiding those keywords, which would lead into problems.
First things first, you need to double your underscores:
def __init__(self, ...):
// whatever
def __str__(self, ...):
// whatever
and lowercase your Class: class Course instead of Class Course.
Now you can use your class like this:
course = Course('Math101', 'Dr. Know Nothing', 'Learning is Fun')
print course
I have two related models:
class FirstModel(models.Model):
base_value = models.FloatField()
class SecondModel(models.Model):
parent = models.ForeignKey(FirstModel)
#property
def parent_value(self):
return self.parent.base_value
#property
def calculate(self):
return self.parent_value + 1
In general, SecondModel.calculate is mostly used in the context of its related FirstModel. However, I sometimes want to be able to call calculate with a temporary value as its parent_value. Something like this:
foo = SecondModel()
# would look in the database for the related FirstModel and add 1 to its base_value
foo.calculate
foo.parent_value = 10
foo.calculate # should return 11
Obviously you can't do this because the parent_value is a read-only property. I also have many different models similar to SecondModel that needs to have this kind of capability.
I've thought about and tried several things, but none have quite seemed to work:
1) Writing a Django proxy model - possible, but the number of objects is rather high, so I'd be writing a lot of similar code. Also, there appears to be a bug related to overriding properties: https://code.djangoproject.com/ticket/16176. But it'd look like this:
class ModelProxy(SecondModel):
class Meta:
proxy = True
def __init__(self, temp_value):
self.parent_value = temp_value
2) Overloading the parent_value property on the instance - like this:
foo = SecondModel()
setattr(foo, 'parent_value', 10)
but you can't do this because properties are members of the class, not the instance. And I only want the temporary value to be set for the instance
3) Metaclass or class generator? - Seems overly complicated. Also, I am uncertain what would happen if I used a metaclass to dynamically generate classes that are children of models.Model. Would I run into problems with the db tables not being in sync?
4) Rewriting the properties with proper getters and setters? - maybe the solution is to rewrite SecondModel so that the property can be set?
Any suggestions?
I believe a mixin would achieve what you want to do, and provide a simple and reusable way of supporting temporary values in your calculations. By mixing the below example into each model you want this behaviour on you can then:
Set a temporary parent value on each model
When calculate is called, it will check whether there is a property parent_value available, and if not it will use the temporary parent value in the calculation.
The code below should achieve what you are looking for - apologies I haven't been able to test it yet but it should be about right - please let me know if any problems that need editing.
class CalculateMixin(object):
#property
def temp_parent_value(self):
return self._temp_parent_value
#temp_parent_value.setter
def temp_parent_value(self, value):
self._temp_parent_value = value
#property
def calculate(self):
parent_value = self.parent_value if self.parent_value else self.temp_parent_value
return parent_value + 1
class SecondModel(models.Model, CalculateMixin):
parent = models.ForeignKey(FirstModel)
self.temp_parent_value = 'Whatever value you desire'
#property
def parent_value(self):
return self.parent.base_value
You can use the property setter:
class SecondModel(models.Model):
_base_value = None
parent = models.ForeignKey(FirstModel)
#property
def parent_value(self):
if self._base_value is None:
return self.parent.base_value
else:
return self._base_value
#parent_value.setter
def parent_value(self, value):
self._base_value = value
#property
def calculate(self):
return self.parent_value + 1
I think you can do what you need to using the mixin PropertyOverrideMixin shown below which, if some property value isn't available, then it will look for the same property prefixed with temp_. This will allow you to provide temporary values that can be used when the real property values can't be looked up.
Below is the mixin, some example models and a unit test to show how this can work. Hopefully this can be adapted for your problem! Finally it is worth mentioning that the properties here can be interchanged with normal object attributes and it should still all work.
from unittest import TestCase
class PropertyOverrideMixin(object):
def __getattribute__(self, name):
"""
Override that, if an attribute isn't found on the object, then it instead
looks for the same attribute prefixed with 'temp_' and tries to return
that value.
"""
try:
return object.__getattribute__(self, name)
except AttributeError:
temp_name = 'temp_{0}'.format(name)
return object.__getattribute__(self, temp_name)
class ParentModel(object):
attribute_1 = 'parent value 1'
class Model(PropertyOverrideMixin):
# Set our temporary property values
#property
def temp_attribute_1(self):
return 'temporary value 1'
#property
def temp_attribute_2(self):
return 'temporary value 2'
# Attribute 1 looks up value on its parent
#property
def attribute_1(self):
return self.parent.attribute_1
# Attribute 2 looks up a value on this object
#property
def attribute_2(self):
return self.some_other_attribute
class PropertyOverrideMixinTest(TestCase):
def test_attributes(self):
model = Model()
# Looking up attributes 1 and 2 returns the temp versions at first
self.assertEquals('temporary value 1', model.attribute_1)
self.assertEquals('temporary value 2', model.attribute_2)
# Now we set the parent, and lookup of attribute 1 works on the parent
model.parent = ParentModel()
self.assertEquals('parent value 1', model.attribute_1)
# now we set attribute_2, so this gets returned and the temporary ignored
model.some_other_attribute = 'value 2'
self.assertEquals('value 2', model.attribute_2)