Pythonic way to check if a variable was passed as kwargs? - python

visible = models.BooleanField()
owner = models.ForeignKey(User, null=True)
def update_address(**kwargs):
address = Address.objects.get(address=kwargs.get('address'))
try:
address.visible = kwargs.get('visible')
except:
pass
try:
address.owner = kwargs.get('owner')
except:
pass
update_address() should result in nothing happening to address.visible or address.owner.
update_address(owner=None) should delete whatever existing owner object was set.
The thing that's confusing me is how to tell if owner=None was explicitly set so I know to delete the existing owner object, or if it was called without owner set to anything so I should leave the owner as it is.

you can use the "in" keyword to check if the key is there or you can specify the default param in the second argument of the dict.get(key, default) function
if 'visible' in kwargs:
do something
# OR
visible = kwargs.get('visible', False)
Update:
if your super() class (ie the parent model) doesn't take the visible param, you can use the dict.pop(key, default) to extract the param before passing it to the super. I thought this could be useful for you to know as well.
def __init__(self, *args, **kwargs):
visible = kwargs.pop('visible', False)
super().__init__(*args, **kwargs)

You could use in and keys:
if 'visible' in kwargs.keys():
...

You can also make your own default option, if that is more convenient; by making an instance of object you can ensure it's distinct from anything else
no_parm = object() # this object is only used for default parms
def thisfunc( p = no_parm ):
if p is no_parm:
# default was used
def thatfunc(**kwargs):
rateval = kwargs.get('rate', no_parm)
if rateval is not no_parm:
# it could be None, if the functiion wa called
# as thatfunc( rate=None)
#
...
Note the use of is for comparisons - this is possible - and recommended - since we are checking if we have two references to exactly the same object - and not a value equality as == does.

Related

Inheritance from class and override method

I have two classes, one inherits of the other. When I hesitate and re-establish the function get_commande_date I receive the following error:
TypeError: BooksCommande.get_commandes_date() missing 1 required positional argument: 'key'
This is my code:
class BaseCommande(ABC):
def __init__(self, list_of_commande: list) -> NoReturn:
if list_of_commande:
self.list_of_commande = list_of_commande
self.commande_date = None
self.comande_payed = None
self.commande_price = None
self.total_commandes = None
self.process_commande(list_of_commande)
super().__init__()
def get_commandes_date(self, list_of_commande):
return [commande['date_start'] for commande in list_of_commande]
def process_commande(self, list_of_commande):
self.commande_date = self.get_commandes_date(list_of_commande)
def my_dict(self):
return{
"commende_date": self.commande_date}
class BooksCommande(BaseCommande):
def __init__(self, list_of_commande: list) -> NoReturn:
super().__init__(list_of_commande)
self.commande_syplies = None
self.commande_books = None
self.process_books(list_of_commande)
def get_commandes_date(self, list_of_commande, key):
commande_date = []
for commande in list_of_commande:
cmd = {
'date_start': commande['date_start'],
'key': key,
'date_end': commande['date_end'],
}
commande_date.append(cmd)
return commande_date
def get_commande_books(self, books: list):
return 10
def process_books(self, list_of_commande):
self.books_list = self.get_commande_books(list_of_commande)
def my_dict2(self):
return{**super().my_dict(),
"books": self.books_list
}
commande_list = [{"date_start": "10/10/2021", "date_end": "12/15/2019"}]
print(BooksCommande(commande_list).my_dict2())
Is there a way to force BaseCommande to use the new redefined function or not? I really don't know how or from where to start.
The problem is you're attempting to change the number of arguments that get passed to the get_commandes_date() method — something that cannot be done when defining a derived class.
The workaround is to make the argument optional. So in class BaseCommande declare a key parameter:
def get_commandes_date(self, list_of_commande, key):
return [commande['date_start'] for commande in list_of_commande]
And then give it a default value in the derived BooksCommande class version of the method. (I'm not sure what might make sense here, so just made it None.)
def get_commandes_date(self, list_of_commande, key=None):
commande_date = []
for commande in list_of_commande:
cmd = {
'date_start': commande['date_start'],
'key': key,
'date_end': commande['date_end'],
}
commande_date.append(cmd)
return commande_date
As others have explained, the issue with your code is that your subclass, BooksCommande, changes the signature of the get_commandes_date method to be different than the version in the base class, BaseCommande. While that might be a bad idea in an abstract sense, it's not forbidden by Python. The real trouble is that one of BaseCommande's other methods, process_commande, tries to use the old signature, so everything breaks when that it gets called.
There is a fairly direct way to fix this, if you want to do so without dramatically changing the code. The general idea is for the two BaseCommande methods to call each other through a private reference. Even if one is overridden in a subclass, the private reference will remain pointing to the original implementation. Name mangling, with two leading underscores is often useful for this:
class BaseCommande(ABC):
...
def get_commandes_date(self, list_of_commande): # this method will be overridden
return [commande['date_start'] for commande in list_of_commande]
__get_commandes_date = get_commandes_date # private reference to previous method
def process_commande(self, list_of_commande):
self.commande_date = self.__get_commandes_date(list_of_commande) # use it here
This kind of design won't always be correct, so you'll need to figure out if it's appropriate for your specific classes or not. If the fact that process_commande is calls get_commandes_date is supposed to be an implementation detail (and so it should keep behaving the same way, even though the latter method is overridden), then this is a good approach. If the relationship between the methods is part of the class's API, then you probably don't want to do this (since overriding the get_commandes_date method may be a deliberate way to change the results of processess_commande in a subclass).
I think you want the method my_dict to have both my_dict and my_dict2 and have a boolean to trigger whenever you want to use one or the other.
def my_dict(self, trigger=False):
if not Trigger:
return{
"commende_date": self.commande_date}
else:
return{**super().my_dict(),
"books": self.books_list
Put this in place of your old my_dict method
def my_dict(self):
return{
"commende_date": self.commande_date}
Edit to add code

How do I set an initial value for one of my method in a class

I have a class named knob and one of my methods is get_click(click). Right now when I call the method get_click(click) I have to specify a Integer value first in order for the code to execute like so knob.get_click(5000). How would I design this portion so that if I don't specify an Integer value the get_click(click) would run would pass a default value and only change when I add an Integer parameter. I tried adding default values in the constructor, but the get_click(click) function kept asking for an argument.
class knob:
def __init__(self, click = "7000", rotateleft="50", rotateright="50"):
self.click = click
self.rotateleft = rotateleft
self.rotateright = rotateright
def get_click(click):
print("G4 "+ str(click) +"\r\n")
def get_rotateleft(self):
return self.rotateleft
def get_rotateright(self):
return self.rotateright
def get_click(self, click=5000): # Set Default value for click
print("G4 "+ str(click) +"\r\n")

Django - passing argument to custom .save method

I have a custom .save method on my model. The model has a start_date and a last_edited value. If the difference between these values is more than 14 days, it should copy/clone itself on save (instead of saving).
You probably already see the problem: infinite recursion. If the clone saves itself, the values will still differ 14 days and the whole copy process will start anew.
Therefore I want to to pass these copies a 'copy' argument in their .save parameter, in order to prevent them from triggering the copy process themselves.
To this end I've written the following code:
def save(self, *args, **kwargs):
#check if a submission is older than the 'create a new one' threshold
#Create a new one if that is the case
delta = self.last_edited - self.start_date
print(args)
print(kwargs)
if 'copy' in kwargs or 'copy' not in args:
print('cloning!')
if delta.days >= 14:
clone = self
clone.pk = None
clone.save('copy')
super(AssessmentSubmission, self).save(*args, **kwargs)
However, for some reason clone.save(copy) does not pass the 'copy' variable to the .save method. I even added some print statements to print all args and kwarg arguments, but both return empty lists/ dicts.
Does anyone know what I am doing wrong?
Why don't you set a flag field in the model? Cleaner than *kwargs and **args. Something like:
class AssessmentSubmission(models.Model):
'''
Your other fields here
'''
flag_field = models.IntegerField(default=0,blank=True,null=True)
def save(self):
#check if a submission is older than the 'create a new one' threshold
#Create a new one if that is the case
delta = self.last_edited - self.start_date
print(args)
print(kwargs)
if not self.flag_field:
print('cloning!')
if delta.days >= 14:
clone = self
clone.pk = None
clone.flag_field = 1
clone.save('copy')
super(AssessmentSubmission, self).save()

How to create dynamic methods with python?

For my project I need to dynamically create custom (Class) methods.
I found out it is not so easy in Python:
class UserFilter(django_filters.FilterSet):
'''
This filter is used in the API
'''
# legacy below, this has to be added dynamically
#is_field_type1 = MethodFilter(action='filter_field_type1')
#def filter_field_type1(self, queryset, value):
# return queryset.filter(related_field__field_type1=value)
class Meta:
model = get_user_model()
fields = []
But it is giving me errors (and headaches...). Is this even possible?
I try to make the code between #legacy dynamic
One option to do this I found was to create the class dynamically
def create_filter_dict():
new_dict = {}
for field in list_of_fields:
def func(queryset, value):
_filter = {'stableuser__'+field:value}
return queryset.filter(**_filter)
new_dict.update({'filter_'+field: func})
new_dict.update({'is_'+field: MethodFilter(action='filter_'+field)})
return new_dict
meta_model_dict = {'model': get_user_model(), 'fields':[]}
meta_type = type('Meta',(), meta_model_dict)
filter_dict = create_filter_dict()
filter_dict['Meta'] = meta_type
UserFilter = type('UserFilter', (django_filters.FilterSet,), filter_dict)
However, this is giving me
TypeError at /api/v2/users/
func() takes 2 positional arguments but 3 were given
Does anyone know how to solve this dilemma?
Exception Value: 'UserFilter' object has no attribute 'is_bound'
You are getting this error because the class methods you are generating, are not bound to any class. To bound them to the class, you need to use setattr()
Try this on a console:
class MyClass(object):
pass
#classmethod
def unbound(cls):
print "Now I'm bound to ", cls
print unbound
setattr(MyClass, "bound", unbound)
print MyClass.bound
print MyClass.bound()
Traceback:
UserFilter = type('Foo', (django_filters.FilterSet, ), create_filter_dict().update({'Meta':type('Meta',(), {'model':
get_user_model(), 'fields':[]} )})) TypeError: type() argument 3 must
be dict, not None
Now, this is failing because dict.update() doesn't return the same instance, returns None. That can be fixed easily
class_dict = create_filter_dict()
class_dict.update({'Meta':type('Meta',(), {'model': get_user_model(), 'fields':[]})}
UserFilter = type('Foo', (django_filters.FilterSet, ), class_dict))
However, just look how messy that code looks. I recommend to you to try to be
clearer with the code you write even if it requires to write a few extra lines. In the long run, the code will be easier to maintain for you and your team.
meta_model_dict = {'model': get_user_model(), 'fields':[]}
meta_type = type('Meta',(), meta_model_dict)
filter_dict = create_filter_dict()
filter_dict['Meta'] = meta_type
UserFilter = type('Foo', (django_filters.FilterSet,), filter_dict)
This code might not be perfect but it is more readable than the original line of code you posted:
UserFilter = type('Foo', (django_filters.FilterSet, ), create_filter_dict().update({'Meta':type('Meta',(), {'model': get_user_model(), 'fields':[]})}))
And removes a complication on an already kinda difficult concept to grasp.
You might want to learn about metaclasses. Maybe you can overwrite the new method of a class. I can recommend you 1 or 2 posts about that.
Another option is that maybe you are not adding the filters correctly or in a way django doesn't expect? That would explain why you get no errors but none of your functions gets called.
You can use classmethod. Here is example how you can use it:
class UserFilter:
#classmethod
def filter_field(cls, queryset, value, field = None):
# do somthing
return "{0} ==> {1} {2}".format(field, queryset, value)
#classmethod
def init(cls,list_of_fields ):
for field in list_of_fields:
ff = lambda cls, queryset, value, field=field: cls.filter_field(queryset, value, field )
setattr(cls, 'filter_'+field, classmethod( ff ))
UserFilter.init( ['a','b'] )
print(UserFilter.filter_a(1,2)) # a ==> 1 2
print(UserFilter.filter_b(3,4)) # b ==> 3 4
You are asking for:
custom (Class) methods.
So we take an existing class and derive a subclass where you can add new methods or overwrite the methods of the original existing class (look into the code of the original class for the methods you need) like this:
from universe import World
class NewEarth(World.Earth):
def newDirectionUpsideDown(self,direction):
self.rotationDirection = direction
All the other Methods and features of World.Earth apply to NewEarth only you can now change the direction to make the world turn your new way.
To overwrite an existing method of a class is as as easy as this.
class NewEarth(World.Earth):
def leIitRain(self,amount): # let's assume leIitRain() is a standard-function of our world
return self.asteroidStorm(amount) #let's assume this is possible Method of World.Earth
So if someone likes a cool shower on earth he/she/it or whatever makes room for new development on the toy marble the burning way.
So have fun in your way learning python - and don't start with complicated things.
If I got you completely wrong - you might explain your problem in more detail - so more wise people than me can share their wisdom.

Where did I mess up in this program for tracking faction alliances?

I have a program that models kingdoms and other groups (called 'factions' in my code).
class Faction:
def __init__(self, name, allies=[]):
self.name = name
self.allies = allies
def is_ally_of(self, other_faction):
if self in other_faction.allies:
return True
else:
return False
def become_ally(self, other_faction, both_ally=True):
""" If both_ally is false, this does *not* also
add self to other_faction's ally list """
if self.is_ally_of(other_faction):
print("They're already allies!")
else:
self.allies.append(other_faction)
if both_ally == True:
other_faction.become_ally(self, False)
RezlaGovt = Faction("Kingdom of Rezla")
AzosGovt = Faction("Azos Ascendancy")
I want to be able to call a factions become_ally() method to add factions to the ally lists, like this:
RezlaGovt.become_ally(AzosGovt) # Now AzosGovt should be in RezlaGovt.allies,
# and RezlaGovt in AzosGovt.allies
What actually happens is this:
RezlaGovt.become_ally(AzosGovt)
# prints "They're already allies!"
# now AzosGovt is in the allies list of both AzosGovt and RezlaGovt,
# but RezlaGovt isn't in any allies list at all.
Whenever I try to call become_ally(), the code should check to make sure they aren't already allies. This is the part that isn't working. Every time I call become_ally(), it prints "They're already allies!", regardless of if they actually are.
I also tried to use if self in other_faction.allies:, but that had the same problem.
I strongly suspect that the problem is with my use of self, but I don't know what terms to Google for more information.
You can't use mutable arguments as the default argument to a function.
def __init__(self, name, allies=[]):
When the default is used, it's the same list each time, so they have the same allies; mutating one changes the other because they're actually the same thing.
Change to:
def __init__(self, name, allies=None):
if allies is None:
allies = []
Alternatively, copy the allies argument unconditionally (so you're not worried about a reference to it surviving outside the class and getting mutated under the class):
def __init__(self, name, allies=[]):
self.allies = list(allies) # Which also guarantees a tuple argument becomes list
# and non-iterable args are rejected
Change this function.
def is_ally_of(self, other_faction):
if other_faction in self.allies:
return True
else:
return False
Check your own data not that of the passed in object.
Also
def __init__(self, name, allies=[]):
Is a bug waiting to happen. Your allies list will be a static list shared between all instances. Instead use
def __init__(self, name, allies=None):
self.name = name
self.allies = allies or []

Categories

Resources