Is it possible to partially override a method? What I mean for example I have one method and I want to update it with some modifications, but I don't want to change whole method (I couldn't figure out how to update old method by inheriting it, but not entirely changing it). So is it possible to do something like this:
class A(object):
def test(self, num):
tmp = 3
res = tmp + num
print
return res
a = A()
a.test(5)
returns 8
class B(A):
def test(self, num):
tmp = 5
a = super(B, self).test(num)
return a
b = B()
b.test(5)
returns 10
Is there a way to update only tmp value, leaving res = tmp + num, so it would use it as it is defined in A class. Or if I want to update like this, there is only way to rewrite everything in that method(because using super I only get the final value that method returns)?
As suggested I update question with method I try to only partially update it:
def _display_address(self, cr, uid, address, without_company=False, context=None):
'''
The purpose of this function is to build and return an address formatted accordingly to the
standards of the country where it belongs.
:param address: browse record of the res.partner to format
:returns: the address formatted in a display that fit its country habits (or the default ones
if not country is specified)
:rtype: string
'''
# get the information that will be injected into the display format
# get the address format
address_format = address.country_id and address.country_id.address_format or \
"%(street)s\n%(street2)s\n%(city)s %(state_code)s %(zip)s\n%(country_name)s"
args = {
'state_code': address.state_id and address.state_id.code or '',
'state_name': address.state_id and address.state_id.name or '',
'country_code': address.country_id and address.country_id.code or '',
'country_name': address.country_id and address.country_id.name or '',
'company_name': address.parent_id and address.parent_id.name or '',
}
for field in self._address_fields(cr, uid, context=context):
args[field] = getattr(address, field) or ''
if without_company:
args['company_name'] = ''
elif address.parent_id:
address_format = '%(company_name)s\n' + address_format
return address_format % args
So for this method I only need to update/change address_format and args, inserting additional values.
Since tmp is a local variable of A's test function, there is no (portable) way to access it. Either make tmp another parameter of test, and just pass in the appropriate value (possible with a suitable default if none is passed), or make it a field of A and override that field's value in B.
Edit: Seeing the actual code, and learning that you aren't allowed to change it, makes this a lot more difficult. Basically, you would have to copy-paste the superclass's code and just change the parts necessary. The good solutions all require modifying the superclass's method.
Related
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
I am new to python, here is the first part of my graduation project. I want to extract data from MySql 'insert' statements
if tableName == 'accounts':
a = Account(rm_apostrophe(slippedMsgs[0]), rm_apostrophe(slippedMsgs[1]),
rm_apostrophe(slippedMsgs[2]))
print(a.account_id, a.account_name, a.customer_code)
Accounts.append(a)
In the code above, Account is a class used to save data, rm_apostrophe is a method for handle strings.
class Account:
def __init__(self, account_id, account_name, customer_code):
self.account_id = account_id
self.account_name = account_name
self.customer_code = customer_code
def rm_apostrophe(raw_data):
if raw_data is None or raw_data == "Null":
return None
elif raw_data.startswith("'"):
return raw_data[1:-1]
else:
return raw_data
Account is a simple method with only three attributes, so there is no problem with writing this way.
But I have another class named Ticket with 73 attributes, which means that I have to do rm_apostrophe(slippedMsgs[x]) 73 times in
t = Ticket(rm_apostrophe(slippedMsgs[0]), rm_apostrophe(slippedMsgs[1]),
rm_apostrophe(slippedMsgs[2])...etc)
I guess there should be an easier way to help me pass in these parameters, maybe I need to modify the constructor. Hope someone can help me, thank you very much
You can use a list comprehension to create the list of arguments:
args = [rm_apostrophe(x) for x in slippedMsgs]
Then you can pass these arguments using t = Ticket(*args)
The * before the args passes every item in the list as an argument to Ticket
Consider the following code example:
from enum import Enum
class Location(Enum):
Outside = 'outside'
Inside = 'inside'
class Inside(Enum): # TypeError for conflicting names
Downstairs = 'downstairs'
Upstairs = 'upstairs'
How do I make Inside have the value 'inside' whilst also being a nested enum for accessing Downstairs and Upstairs?
Desired input:
print(Location.Inside)
print(Location.Inside.value)
print(Location.Inside.Downstairs)
print(Location.Inside.Downstairs.value)
Desired output:
Location.Inside
inside
Location.Inside.Downstairs
downstairs
UPDATE 1:
Some more context to my specific problem:
class Location(Enum):
Outside = 'outside'
Inside = 'inside'
class Inside(Enum): # TypeError for conflicting names
Downstairs = 'downstairs'
Upstairs = 'upstairs'
class Human:
def __init__(self, location):
self.location = location
def getLocationFromAPI():
# this function returns either 'inside' or 'outside'
# make calls to external API
return location # return location from api in str
def whereInside(human):
if human.location != Location.Inside:
return None
# here goes logic that determines if human is downstairs or upstairs
return locationInside # return either Location.Downstairs or Location.Upstairs
location_str = getLocationFromAPI() # will return 'inside' or 'outside'
location = Location(location_str) # make Enum
human = Human(location) # create human with basic location
if human.location == Location.Inside:
where_inside = whereInside(human)
human.location = where_inside # update location to be more precise
The problem is when I create the Human object I only know of a basic location, as in 'inside' or 'outside'. Only after that can I update the location to be more precise.
You can accomplish this by embedding an enum.Enum inside another like so: (just watch out for names conflicting)
from enum import Enum
class _Inside(Enum):
Downstairs = 'downstairs'
Upstairs = 'upstairs'
class Location(Enum):
Outside = 'outside'
Inside = _Inside
print(Location.Inside.value.Downstairs.value)
downstairs
it may be a bit late and the one who asked the question is no longer necessary, but I leave it here in case someone wants to take a look at it, and even if it has already been validated as one, although the same comment that it is not completely complete .
But I have been thinking about it and in the end I have solved it by looking at the same documentation XD.
You cannot extend classes of Enums, but you can extend methods, I have followed this way and the only thing I have done has been to override the new and init methods, the use case can be modified, this is only to nest enumerators.
from enum import Enum
class SuperNestedEnum(Enum):
def __new__(cls, *args):
obj = object.__new__(cls)
value = None
# Normal Enumerator definition
if len(args) == 1:
value = args[0]
# Have a tuple of values, first de value and next the nested enum (I will set in __init__ method)
if len(args) == 2:
value = args[0]
if value:
obj._value_ = value
return obj
def __init__(self, name, nested=None):
# At this point you can set any attribute what you want
if nested:
# Check if is an Enumerator you can comment this if. if you want another object
if isinstance(nested, EnumMeta):
for enm in nested:
self.__setattr__(enm.name, enm)
class Homework(Enum):
Task = "5"
class Subjects(SuperNestedEnum):
Maths = "maths"
English = "english"
Physics = "nested", Homework
class School(SuperNestedEnum):
Name = "2"
Subjects = "subjects", Subjects
Ignore the use case because it doesn't make sense, it's just an example
>>> School.Name
<School.Name: '2'>
>>> School.Subjects
<School.Subjects: 'subjects'>
>>> School.Subjects.value
'subjects'
>>> School.Subjects.Maths
<Subjects.Maths: 'maths'>
>>> School.Subjects.Physics.value
'nested'
>>> School.Subjects.Physics.Task
<Homework.Task: '5'>
>>> School.Subjects.Physics.Task.value
'5'
If anyone has similar issues and just wants a simple solution for the topic without patching any functions or additional imports for enums containing strings, follow these steps:
Create the value enums, in your lower hierarchy, like:
class __private_enum1__(str, enum.Enum):
VAL11 = "abc"
VAL12 = "def"
class enum2(str, enum.Enum):
VAL21 = "123"
VAL22 = "456"
Create a base class (a container) for these enums. Where you can either import the enums classes or simply directly acccess the enums.
class myValues:
VAL11 = __private_enum1__.VAL11
VAL12 = __private_enum1__.VAL12
VALS2X = enum2
Then you can access your values by:
print(myValues.VAL11.value)
print(myValues.VAL2X.VAL21.value)
.value is not necessary here but it shows that you both access the string inside the enum for passing it to other functions but also the enum itself, which is pretty neat. So basically, first create the values, then the structure. That way you have a class but it provides you the basic functionality of enums and you can nest them as deep as you want to without further imports.
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.
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 []