classmethod inheritance and overwrite class parameter - python

I struggle to understand how to assign a value to PersonProfile from a class method when PersonProfile inherits from PersonBase.
class PersonBase:
def __init__(self, contact_no=None, email=None, house=None, category=None, near=None, house_number=None, road=None,
unit=None, level=None, staircase=None, entrance=None, po_box=None, postcode=None, suburb=None,
city_district=None, city=None, island=None, state_district=None, state=None, country_region=None,
country=None, world_region=None):
self.contact_no = contact_no
self.email = email
self.house = house
self.category = category
self.near = near
self.house_number = house_number
self.road = road
self.unit = unit
self.level = level
self.staircase = staircase
self.entrance = entrance
self.po_box = po_box
self.postcode = postcode
self.suburb = suburb
self.city_district = city_district
self.city = city
self.island = island
self.state_district = state_district
self.state = state
self.country_region = country_region
self.country = country
self.world_region = world_region
class PersonProfile(PersonBase):
#classmethod
def from_string(cls, full_address):
"""
Takes raw, unformatted address string and creates a PersonProfile class out of it.
:param full_address: unformatted address coming from a DB
:type full_address: str
:return: PersonProfile class
:rtype: PersonProfile
"""
if full_address == "":
# return empty PersonProfile cls with all its defaults
return cls(PersonProfile)
elif full_address is None:
raise TypeError("Provided object must be of type string")
# extract phone numbers
_contact_no = PersonProfile.find_phone_no(full_address)
if len(_contact_no) != 0:
cls.contact_no = ",".join(_contact_no)
for c in _contact_no:
full_address = full_address.replace(c, '')
return cls()
So when I try to assign a phone number to PersonProfile.contact_no using cls.contact_no = ",".join(_contact_no) it doesn't seem to have any effect. What is a correct way to do so?
Also, at the end of that class method how would I return all values including the one I've overwritten during classmethod execution? I'm using return cls() but doesn't seem to be working either.
I'd like to make it work like so:
p1 = PersonProfile.from_string("(+22) 936107349")
print(p1.contact_no)
--> (+22) 936107349
print(p1.city)
--> None

You want to provide the parsed data to the __init__ method; by calling cls(**key_word_arguments), the dictionary of kwargs returned by the staticmethod parse_full_address is passed the constructor.
I suggest that you write a parser, and validators for the data separate from the class factory, and pass to the factory arguments that have been thoroughly extracted.
class PersonProfile(PersonBase):
#classmethod
def from_string(cls, full_address):
"""
Takes raw, unformatted address string and creates a PersonProfile class out of it.
:param full_address: unformatted address coming from a DB
:type full_address: str
:return: PersonProfile class
:rtype: PersonProfile
"""
kwargs = PersonProfile.parse_full_address(full_address)
return cls(**kwargs)
#staticmethod
def parse_full_address(full_address):
"""parses the full_address and returns the parsed data as a dictionary
currently a stub that returns test data
"""
return {'contact_no': '(+22) 936107349', 'email': 'a#a.com', 'house': 'Ze Mansion', 'city': 'Dummy Data'}
p1 = PersonProfile.from_string("(+22) 936107349")
print(p1.contact_no)
print(p1.city)

Related

Anaconda Spyder - Class Attributes Autocompletion

I have a couple of Classes defined.
Both of these classes take a json file as input, and extract the data into a class instance, and places them into a dictionary.
So all of my EmployeeProfile instances are stored in a dictionary called SP, with the employees email as the key, and the class instance as the value.
All of my RiskProfile instances are stored in a dictionary called RISKS, with the risk_ID as the key and the class instance as the value.
class EmployeeProfile:
def __init__(self, profile):
self.displayname = profile.get('displayName')
self.email = profile.get('email')
self.firstname = profile.get('firstName')
self.surname = profile.get('surname')
self.fullname = profile.get('fullName')
self.costcode = profile.get('work').get('custom')\
.get('Cost Category_t9HHc')
self.title = profile.get('work').get('title')
self.department = profile.get('work').get('department')
self.city = profile.get('work').get('site')
self.id = profile.get('work').get('employeeIdInCompany')
self.manageremail = ''
self.costline = 'N/A'
self.groups = {}
self.attest = {}
class RiskProfile:
def __init__(self, risk_profile):
self.ID = risk_profile.get('ID')
self.key = risk_profile.get('Key')
self.name = risk_profile.get('Name')
self.description = risk_profile.get('Description', '')
self.owner = risk_profile.get("Owner_DisplayName")
self.assignedto = risk_profile.get("AssignedTo_DisplayName")
self.email = None
self.costline = ''
self.notes = ''
self.assessmentlevel = int(risk_profile.get("AssessmentLevel"))
self.rating = ''
self.workflow = risk_profile.get('WorkflowState')
Now when I do something like:
for profile in SP:
print(SP[profile].{here i see the attribute of the class instance})
So I can see a selection of the attributes I may want to print or change etc...
print(SP[profile].department)
or
print(SP[profile].name)
However when I do the same for RiskProfile instances I do not get the list of attributes.
If I enter them manually my code still works, but does anyone know why this is not working the same way?
for profile in RISKS:
print(RISK[profile].{I never get a list of attributes})
I use Anaconda with Spyder.

Can't loop through dict_keys in #property method in python 3?

So I have the following code:
#property
def mod_list(self) -> List[Modifier]:
mods = []
print(len(self.statuses)) #Prints 0??? Update method prints the actual number when called??? Also means it *is* getting called properly when it's getting accessed
for status in self.statuses: # I've tried calling the keys() method on the dict but that doesn't work either
print("hello") #Doesn't print, indicating that it isn't looping
mods.extend(status.mods) # Note: statuses dict uses StatusEffect objects as keys, with values being the number of turns left before that status is removed; StatusEffects all possess a 'mods' property that is initialized to '[]' and can only be made up of modifiers
return mods
I don't understand why it can't access the keys of the dict? Even if I remove the decorator and call it instead of accessing it?
Especially when this method works properly?
def update(self):
deletion = []
print(len(self.statuses)) #Prints actual number of keys????
for name in self.statuses.keys():
print(name.name, self.statuses[name]) #Prints normally whenever update is called???
if hasattr(name, "turn_effect"):
name.turn_effect(self.entity)
self.statuses[name] -= 1
if self.statuses[name] < 1:
deletion.append(name)
...
for status in deletion:
del self.statuses[status]
Why isn't it working properly? And how do I fix it?
Edit: I managed to recreate the issue below, I think it might have to do with 'deepcopy' in the spawn method since I couldn't recreate the issue from scratch until I implemented and used the spawn method.
from __future__ import annotations
from typing import Dict, List
from copy import copy, deepcopy
class Entity:
def __init__(self, name:str, **kwargs:Component):
self.name = name
self.components:Dict[str, Component] = {}
for name, component in kwargs.items():
self.add_component(name, component)
def add_component(self, name:str, component:Component):
self.components[name] = component
component.entity = self
def update(self):
for comp in self.components.values():
comp.update()
def spawn(self):
return deepcopy(self)
class Component:
__entity: Entity
#property
def entity(self) -> Entity:
return self.__entity
#entity.setter
def entity(self, entity:Entity):
if hasattr(self, "__entity") and self.__entity is not None:
self.entity.remove_component(self)
self.__entity = entity
def update(self):
"""Placeholder method for component update methods"""
class StatusList(Component):
entity: Entity
def __init__(self) -> None:
self.statuses:Dict[StatusEffect, int] = {}
def add_status(self, status:StatusEffect, turns:int=1):
self.statuses[status] = turns
def update(self):
deletion = []
print(len(self.statuses.keys()))
for name in self.statuses.keys():
print(name.name, self.statuses[name])
if hasattr(name, "turn_effect"):
name.turn_effect(self.entity)
self.statuses[name] -= 1
if self.statuses[name] < 1:
deletion.append(name)
for status in deletion:
del self.statuses[status]
#property
def mod_list(self) -> List[Modifier]:
mods = []
print(len(self.statuses))
for status in self.statuses:
print("hello")
mods.extend(status.mods)
return mods
class StatusEffect:
name:str
turn_effect: function
mods:List[Modifier] = []
def apply(self, entity:Entity, turns:int=1):
if "status_list" in entity.components.keys():
entity.components["status_list"].add_status(self.copy(), turns)
def copy(self): #I specifically defined this method in the original code in case I need to modify it in the future
return copy(self)
class StatList(Component):
entity: Entity
stat_record: List[Stat] = []
def __init__(self, **stats:Stat) -> None:
for name, stat in stats.items():
stat.stat_list = self
stat.name = name
self.stat_record.append(stat)
def get_stat(self, name:str) -> Optional[Stat]:
for stat in self.stat_record:
if name == stat.name:
return stat
def get_stat_name(self, stat:Stat) -> Optional[str]:
if stat in record:
return stat.name
class Stat:
name:str
base_value:int
def __init__(self, base:int=0):
self.base_value = base
#property
def entity(self) -> Entity:
return self.stat_list.entity
#property
def current_value(self) -> int:
value = self.base_value
for mod in self.get_modifiers():
value += mod.value
return int(value)
def get_modifiers(self):
for component in self.entity.components.values():
if hasattr(component, "mod_list"):
for mod in component.mod_list:
if mod.stat == self.name:
yield mod
class Modifier:
stat: str
value: Union[int, float]
def __init__(self, stat:str, value:Union[int, float]):
self.stat = stat
self.value = value
rage = StatusEffect()
rage.name = "Rage"
rage.turn_effect = lambda entity : print(f"{entity.name} is enraged")
rage.mods = [
Modifier("atk", 5)
]
player = Entity(
name="Player",
stat_list=StatList(atk=Stat(5)),
status_list=StatusList()
).spawn()
rage.apply(player, 10)
while True:
player.update()
player.components["stat_list"].get_stat("atk").current_value
input()
Unfortunately, using copy() in the spawn method would result in entities created that way sharing status effects, stats, etc., which really defeats the purpose of spawning new entities
Edit 2: Modified spawn method to use copy and to copy all components, have to add guard clauses now but it works.

Calling class method but got 'str' object has no attribute 'method' error

I have a function to check whether my list of class objects meet a certain criteria, and if they do, they will be stored in a list.
Here is the function:
def filter_stories(stories, triggerlist):
"""
Takes in a list of NewsStory instances.
Returns: a list of only the stories for which a trigger in triggerlist fires.
"""
print('checkpoint6, triggerlist', triggerlist)
fired_trigger = []
for trig in triggerlist:
print('checkpoint7, trig', trig)
for story in stories:
print('checkpoint8, story', story)
if trig.evaluate(story):
print('checkpoint9, calling evaluate method', trig.evaluate(story))
fired_trigger.append(story)
return fired_trigger
just to illustrate that they are indeed class objects. Here are the results from my terminal:
checkpoint6, triggerlist [<__main__.AndTrigger object at 0x0000026C2655A248>, <__main__.AndTrigger object at 0x0000026C26575D88>]
checkpoint7, trig <__main__.AndTrigger object at 0x0000026C2655A248>
checkpoint8, story <__main__.NewsStory object at 0x0000026C26576188>
but when I tried to call method of the class object in this line if trig.evaluate(story):, I get this error 'str' object has no attribute 'evaluate'...
Here is my class and subclasses definitions if it is helpful.
class NewsStory:
def __init__(self, guid, title, description, link, pubdate):
self.guid = guid
self.title = title
self.description = description
self.link = link
self.pubdate = pubdate
def get_guid(self):
return self.guid
def get_title(self):
return self.title
def get_description(self):
return self.description
def get_link(self):
return self.link
def get_pubdate(self):
return self.pubdate
# ======================
# Triggers
# ======================
class Trigger(object):
def evaluate(self, story):
"""
Returns True if an alert should be generated
for the given news item, or False otherwise.
"""
# DO NOT CHANGE THIS!
raise NotImplementedError
# PHRASE TRIGGERS
class PhraseTrigger(Trigger):
def __init__(self, phrase):
self.phrase = phrase.strip().lower()
def is_phrase_in(self, text):
result = []
phrase_list = self.phrase.split()
phrase = " ".join(phrase_list)
for punct in string.punctuation:
if punct in text:
text = text.replace(punct, " ")
text_list = text.lower().split()
text = " ".join(text_list)
result.append(re.search(fr'\b{phrase}\b', text))
if None in result:
return False
return True
def __str__(self):
print('TitleTrigger!')
class TitleTrigger(PhraseTrigger):
def evaluate(self, story):
return self.is_phrase_in(story.get_title())
class DescriptionTrigger(PhraseTrigger):
def evaluate(self, story):
return self.is_phrase_in(story.get_description())
# TIME TRIGGERS
class TimeTrigger(Trigger):
def __init__(self, date):
try: # if the date supplied is string, strip it into datetime and replace the tz into EST
date_obj = datetime.strptime(date, "%d %b %Y %H:%M:%S")
self.time = datetime.replace(date_obj, tzinfo=pytz.timezone("EST"))
except TypeError: # else if it is already in datetime object, replace the tz
self.time = datetime.replace(date, tzinfo=pytz.timezone("EST"))
class BeforeTrigger(TimeTrigger):
def evaluate(self, story):
story_date = story.get_pubdate()
try: # if the date supplied is string, strip it into datetime and replace the tz into EST
story_date_obj = datetime.strptime(story_date, "%d %b %Y %H:%M:%S")
story_time = datetime.replace(story_date_obj, tzinfo=pytz.timezone("EST"))
except TypeError: # else if it is already in datetime object, replace the tz
story_time = datetime.replace(story_date, tzinfo=pytz.timezone("EST"))
if self.time > story_time:
return True
return False
class AfterTrigger(TimeTrigger):
def evaluate(self, story):
story_date = story.get_pubdate()
try: # if the date supplied is string, strip it into datetime and replace the tz into EST
story_date_obj = datetime.strptime(story_date, "%d %b %Y %H:%M:%S")
story_time = datetime.replace(story_date_obj, tzinfo=pytz.timezone("EST"))
except TypeError: # else if it is already in datetime object, replace the tz
story_time = datetime.replace(story_date, tzinfo=pytz.timezone("EST"))
if self.time < story_time:
return True
return False
# COMPOSITE TRIGGERS
class NotTrigger(Trigger):
def __init__(self, other_trigger):
self.other = other_trigger
def evaluate(self, story):
return not self.other.evaluate(story)
class AndTrigger(Trigger):
def __init__(self, trigger1, trigger2):
self.trigger1 = trigger1
self.trigger2 = trigger2
def evaluate(self, story):
return self.trigger1.evaluate(story) and self.trigger2.evaluate(story)
class OrTrigger(Trigger):
def __init__(self, trigger1, trigger2):
self.trigger1 = trigger1
self.trigger2 = trigger2
def evaluate(self, story):
return self.trigger1.evaluate(story) or self.trigger2.evaluate(story)
Here is the complete error traceback:
checkpoint6, triggerlist [<__main__.AndTrigger object at 0x000001DA9D77A088>, <__main__.AndTrigger object at 0x000001DA9D795C88>]
checkpoint7, trig <__main__.AndTrigger object at 0x000001DA9D77A088>
checkpoint8, story <__main__.NewsStory object at 0x000001DA9D799208>
'str' object has no attribute 'evaluate'
Process finished with exit code -1
I don't understand why it is giving me this error as the print statement shows it is an object and not of type string.. and so, very much clueless on how to fix this..
Sorry for the basic question and thank you for your help. :)
In case this might be of help to anyone who is facing the same error, my problem was when I parsed string as the arguments to my class constructor instead of the intended object type.
This is the class definition:
class PhraseTrigger(Trigger):
def __init__(self, phrase):
self.phrase = phrase.strip().lower()
def is_phrase_in(self, text):
result = []
phrase_list = self.phrase.split()
phrase = " ".join(phrase_list)
for punct in string.punctuation:
if punct in text:
text = text.replace(punct, " ")
text_list = text.lower().split()
text = " ".join(text_list)
result.append(re.search(fr'\b{phrase}\b', text))
if None in result:
return False
return True
class DescriptionTrigger(PhraseTrigger):
def evaluate(self, story):
return self.is_phrase_in(story.get_description())
class AndTrigger(Trigger):
def __init__(self, trigger1, trigger2):
self.trigger1 = trigger1 # should be class object
self.trigger2 = trigger2 # should be class object
def evaluate(self, story):
return self.trigger1.evaluate(story) and self.trigger2.evaluate(story)
Here is the function where I instantiate the classes objects:
lines = ['t2,DESCRIPTION,Hillary Clinton', 't3,DESCRIPTION,Donald Trump', 't5,AND,t2,t3']
func_dict = {'AND': AndTrigger,
'DESCRIPTION': DescriptionTrigger} # to map the keyword to class constructor
trigger_dict = {} # to store all created class objects
for line in lines:
split = line.split(',')
if split[1] != 'AND':
trigger_dict[split[0]] = func_dict[split[1]](split[2])
if split[1] == 'AND':
trigger_dict[split[0]] = func_dict[split[1]](trigger_dict[split[2]], trigger_dict[split[3]])
So, I have a dictionary called func_dict where I store the class constructor as the value to its corresponding key (keyword which I will use to search in lines). I then iterate over lines and instantiate the class objects depending on the second element of each line. If the second element is 'AND', I wanted to create a new object of AndTrigger with class object t2 and t3 as arguments.
trigger1 and trigger2 (see AndTrigger class definition) should be class object but I initially instantiated it like so:
trigger_dict[split[0]] = func_dict[split[1]](split[2], split[3])
here, split[2] is 't2' and split[3] is 't3' from here 't5,AND,t2,t3'. They are of type string.
Instead, I should have parsed the instantiated class object stored in trigger_dict as the argument.
trigger_dict[split[0]] = func_dict[split[1]](trigger_dict[split[2]], trigger_dict[split[3]])
Hope that helped!

Adding to a class dynamically

I've created a new class and I'm trying to add to that class dynamically,
I've created a list, that I want to put multiple objects in, then I will iterate over that list in Django (is this the correct way of doing things?)
but I'm getting the below error
TypeError: __init__() takes exactly 9 arguments (1 given)
I know what the error means, I'm just wonder how I go about creating a new instance of my objects and adding to it on the fly easily?
### create User Object
class User:
def __init__(self, Policy, Level, StartDate, EndDate, StartTime, EndTime, Name, Mobile):
self.Policy = Policy
self.Level = Level
self.StartDate = StartDate
self.EndDate = EndDate
self.StartTime = StartTime
self.EndTime = EndTime
self.Name = Name
self.Mobile = Mobile
def __init__(self):
pass
### Get all the Polices ###
lstOnCall = []
for objPolicy in objPolicyData['escalation_policies']:
strPolicyName = objPolicy['name']
if strPolicyName.lower().find('test') == -1:
for objOnCall in objPolicy['on_call']:
objUser = User()
objUser.Policy = strPolicyName
objUser.Level = objOnCall['level']
objUser.StartDate = getDate(objOnCall['start'])
objUser.EndDate = getDate(objOnCall['end'])
objUser.StartTime = getTime(objOnCall['start'])
objUser.EndTime = getTime(objOnCall['end'])
objUser = objOnCall['user']
objUser.Name = objUser['name']
objUser.Mobile = getUserMobile(objUser['id'])
lstOnCall.append(objUser)
print lstOnCall
UPDATE:
adding the below works, i just need to know how to print the items now?
def __init__(self):
pass
the below
for item in lstOnCall:
print item()
returns
print item()
AttributeError: User instance has no __call__ method
You can write a dynamic constructor (def __init__) for your class so:
class User(object):
__attrs = ['Policy', 'Level', 'StartDate', 'EndDate', 'StartTime',
'EndTime', 'Name', 'Mobile']
def __init__(self, **kwargs):
for attr in self.__attrs:
setattr(self, attr, kwargs.get(attr, None))
def __repr__(self):
return ', '.join(
['%s: %r' % (attr, getattr(self, attr)) for attr in self.__attrs])
The variable __attrs stores the variables names. I used double underscore variable, so that it's inaccessible from extend.
user = User()
print(user.__attrs)
Traceback (most recent call last):
print(user.__attrs)
AttributeError: 'User' object has no attribute '__attrs'
Yes, there are other method to access double underscore variable, but no one will do that ;)
The function __repr__ return the string by calling print or str, if the function __str__ doesn't exist.
Now test it
>>> u1 = User(Name='user1')
>>> u2 = User(Name='user2', Policy=1, Level=3)
>>> print(u1)
Policy: None, Level: None, StartDate: None, EndDate: None, StartTime: None, EndTime: None, Name: 'user1', Mobile: None
>>> print(u2)
Policy: 1, Level: 3, StartDate: None, EndDate: None, StartTime: None, EndTime: None, Name: 'user2', Mobile: None
If you use my codes, you can print the items in your case so:
for item in lstOnCall:
print item
Other problem of your code
There aren't the definition Function overloading in Python. You can define multiple function with the same name in python. But it doesn't make any sense. Only the last definition remains in your class/module. The previous definitions will be overwritten. What you are doing with
class User:
def __init__(self, a, b, c):
...
def __init__(self):
pass
is False. It works in Java or C# but not in Python. The function def __init__(self, a, b, c) will be overwritten. Only the function def __init__(self) exists in your class.
You could set all of the parameters to __init__ to be None by default:
def __init__(self, Policy=None, Level=None, etc...):
Convert the positional parameters of your constructor method to named, optional parameters with a useful default value:
class User:
def __init__(self, Policy=Null, Level=1,
StartDate="2016-01-01", EndDate="2016-12-31",
StartTime="00:00", EndTime="23:59",
Name="UNKNOWN", Mobile=""):
self.Policy = Policy
self.Level = Level
self.StartDate = StartDate
self.EndDate = EndDate
self.StartTime = StartTime
self.EndTime = EndTime
self.Name = Name
self.Mobile = Mobile
Try this,
class User:
def __init__(self,*args,**kargs):
if len(kargs)==0 : ''' No param passed '''
self.Policy = 'Some'
self.Level = 0
else:
self.Policy = kargs['Policy']
self.Level = kargs['Level']
[..]
user= User()
user1= User(Policy='Some',Level=13)

I cannot understand a case of passing an object as a parameter

I have a class Node with a function defined
class Node(object):
def __init__(self, index, state = None, input = None, directed_neighbours=False):
"""
Parameters
----------
index : int
Node index. Must be unique in the graph.
"""
self._input = input
self.state = state
#self._status = 'active'
self._index = int(index)
self._neighbours = set()
self._port_count = 0
self._ports = []
if directed_neighbours:
self._predecessors = set()
self._successors = self._neighbours
self._directed_neighbours = True
else:
self._successors = self._neighbours
self._predecessors = self._neighbours
self._directed_neighbours = False
#property
def setStatus(self, status):
self._status = status
I have another function
def init(node):
node.setStatus('active')
Now, I have a class
class DistAlgo:
def __init__(self, name, initFunc, states, messages, sendFunc, receiveFunc, stoppingCheck):
self.name = name
#self.inputGraph = inputGraph
self.initFunc = initFunc
self.states = states
self.messages = messages
self.sendFunc = sendFunc
self.receiveFunc = receiveFunc
self.comm_round = 0
self.stoppingCheck = stoppingCheck
def run(self, inputGraph):
for node in inputGraph.nodes:
print('hello', node)
node.state = self.initFunc(node)
<....more code...>
When I create an object of DistAlgo
myalgo = DistAlgo('BMM', init, states, messages, send, receive, stoppingCheck)
and then call its run function:
myalgo.run(problemGraph)
I get an error in the init function above, as:
TypeError: setStatus() missing 1 required positional argument: 'status'
I surely am doing more than one thing wrong I guess, as this is my first Python try. Please point them out!
Properties work a bit differently:
#property
def status(self):
return self._status
#status.setter
def status(self, status):
self._status = status
Now you can set the value with an assignment:
node.status = 'active'

Categories

Resources