Question about multiple inheritance, dynamic class creating and instantiation - python

Hello I have the following situation:
A specialized class that inherits from two parent class
The need to define the most specialized class at run time, based on some information that I get only when I start reading data from a database.
I defined the following code to handle the create all the classes in the chain:
class BusinessDocument():
#staticmethod
def get_class(doc_type):
switch = {
'MasterData': MasterData,
'Transactional': Transactional
}
func = switch.get(doc_type, lambda: "Invalid Noun Type")
return func()
def __init__(self, doc_id, location, doc_type):
self.doc_id = doc_id
self.location = location
self.doc_type = doc_type
pass
#property
def get_location(self):
return self.location
#property
def get_doc_id(self):
return self.doc_id
class MasterData(BusinessDocument):
def __init__(self, doc_id, location):
BusinessDocument.__init__(self, doc_id, location, 'MasterData')
class Transactional(BusinessDocument):
def __init__(self, doc_id, location):
BusinessDocument.__init__(self, doc_id, location, 'Transactional')
class NounClass():
#staticmethod
def get_class(doc_name, doc_type):
return type(doc_name, (BusinessDocument.get_class(doc_type),
BusinessDocument, ),dict.fromkeys(['doc_id', 'location']))
Then at run time when I get the doc_name and I try to create a new class. At this point I may not have the required arguments doc_id and location but I need to class type.
invoice_cls = NounClass.get_class('Invoice', 'Transactional')
I get the following error:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-10-cb774746875a> in <module>
----> 1 invoice_cls = NounClass.get_class('Invoice', 'Transactional')
<ipython-input-9-aa5e0b316ed1> in get_class(doc_name, doc_type)
35 #staticmethod
36 def get_class(doc_name, doc_type):
---> 37 return type(doc_name, (BusinessDocument.get_class(doc_type),
38 BusinessDocument, ),dict.fromkeys(['doc_id', 'location']))
<ipython-input-9-aa5e0b316ed1> in get_class(doc_type)
7 }
8 func = switch.get(doc_type, lambda: "Invalid Noun Type")
----> 9 return func()
10
11 def __init__(self, doc_id, location, doc_type):
TypeError: __init__() missing 2 required positional arguments: 'doc_id' and 'location'
I understand that the reason for it is because the __init__() will be called during the class instantiation, but I thought that type would be only creating a new type and not instantiate one right away. So my question is if is there a way to defer the instantiation of the instance at this time.
Thank you in advance for any help and tips on this.
--MD.

The initalization occurs on line 9:
return func()
I assume you want to return a class object, so remove those parantheses.
Also func is misleding, I've changed it to cls:
def get_class(doc_type):
switch = {
'MasterData': MasterData,
'Transactional': Transactional
}
cls = switch.get(doc_type, lambda: "Invalid Noun Type")
return cls

Related

Python inheritance, having set_up() method that's always called by base class init while having varying arguments in child classes

I want to achieve something like this. But python gives error of missing argument resource due to the fact Node calls set_up without argument. What's the correct way to do it?
Basically I want to factor out the initialisation of a class into set_up and at the same time having this set_up method callable when provided with the necessary resources. The child class requires resources that's not required by base, and at the same time the set_up should always called by init as it does the actually initialisation.
--------edit---------
I will try to explain my motivation better. I want to have a set_up method that does the initialisation of Node so I can re-initialise a Node during runtime. And I want to provide certain resources through this set_up in MyNode which aren't required by Node. So the ideal behavior is that
MyNode("node", resource="water").name == "node_altered"
MyNode("node", resource="water").resource == "water"
MyNode("node").set_up("water").resource == "water"
MyNode("node").set_up("water").set_up("fire") == "fire"
MyNode("node").set_up("water").set_up().resource == "water"
this is my try:
class Node:
def __init__(self, name):
self.name = name
self.set_up()
def set_up(self):
self.name = name + "_altered"
class MyNode(Node):
def __init__(self, name, resource=None):
super().__init__(name)
self.foo = None
self.resource = resource
def set_up(self, resource):
super().set_up()
self.foo = "foo"
self.resource = resource
return self
MyNode("foo").set_up(2).resource
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-70-01756d189150> in <module>
17 return self
18
---> 19 MyNode("foo", "resource").set_up(2).resource
<ipython-input-70-01756d189150> in __init__(self, name, resource)
9 class MyNode(Node):
10 def __init__(self, name, resource):
---> 11 super().__init__(name)
12 self.resource = None
13
<ipython-input-70-01756d189150> in __init__(self, name)
2 def __init__(self, name):
3 self.name = name
----> 4 self.set_up()
5
6 def set_up(self):
TypeError: set_up() missing 1 required positional argument: 'resource'
Why not define a default None value for MyNode.set_up as well ?
[...]
[...]
def set_up(self, resource=None):
if resource is None:
raise TypeError(
"set_up() 'resource' argument cannot be None"
)
super().set_up()
self.resource = resource
return self
Are you trying to do something like this:
class Node:
def __init__(self, name):
self.name = name
def set_up(self):
print(self.name)
class MyNode(Node):
def set_up(self, resource):
super().set_up()
self.resource = resource
return self
print(MyNode('bar').set_up('foo').resource)
print('----')
print(MyNode("bar").set_up('bar').resource)
print('----')
print(MyNode("bar").set_up().resource)
output
bar
foo
----
bar
bar
----
Traceback (most recent call last):
File "main.py", line 22, in <module>
print(MyNode("bar").set_up().resource)
TypeError: set_up() missing 1 required positional argument: 'resource'
Edit :
As per OP comment:
What you are trying to achieve is not possible with the way you designed this.
You have to give up at either the mandatory resource in MyNode or calling set_up from __init__ because of the method resolution order.
Node.__init___ will call the set_up and pass arguments to it, now the arguments can be made to be dynamic but you cannot restrict the arguments as when you call the constructor for the first time it will not have the resource parameter and throw an error.
Another solution is to not call super in MyNode.__init__ and MyNode.set_up and there handle all the logic there, but then would you even need to inherit from node?!

Issues passing self to class decorator in python

I am new to decorators but ideally I wan to use them to simply define a bunch of class functions within class OptionClass, each representing some particular option with a name and description and if it's required. I don't want to modify the operation of the class function at all if that makes sense, I only want to use the decorator to define name, description, and if it's required.
Problem 1: I construct an OptionClass() and I want to call it's option_1. When I do this I receive a TypeError as the call decorator is not receiving an instance of OptionClass. Why is this? When I call option_1 passing the instance of OptionClass() it works. How do I call option_1 without needing to always pass the instance as self.
The error when received is:
Traceback (most recent call last):
File "D:/OneDrive_P/OneDrive/projects/python/examples/dec_ex.py", line 110, in <module>
print(a.option_1("test")) # TypeError: option1() missing 1 required positional argument: 'test_text'
File "D:/OneDrive_P/OneDrive/projects/python/examples/dec_ex.py", line 80, in __call__
return self.function_ptr(*args, **kwargs)
TypeError: option_1() missing 1 required positional argument: 'test_text'
Problem 2: How would I run or call methods on the decorator to set_name, set_description, set_required?
Problem 3: Although this is a sample I intend to code an option class using async functions and decorate them. Do I need to make the decorator call be async def __call__() or is it fine since it's just returning the function?
class option_decorator(object):
def __init__(self, function_pt):
self.function_ptr = function_pt
self.__required = True
self.__name = ""
self.__description = ""
def set_name(self, text):
self.__name = text
def set_description(self, text):
self.__description = text
def set_required(self,flag:bool):
self.__required = flag
def __bool__(self):
"""returns if required"""
return self.__required
def __call__(self, *args, **kwargs):
return self.function_ptr(*args, **kwargs)
def __str__(self):
"""prints a description and name of the option """
return "{} - {}".format(self.__name, self.__description)
class OptionClass(object):
"""defines a bunch of options"""
#option_decorator
def option_1(self,test_text):
return("option {}".format(test_text))
#option_decorator
def option_2(self):
print("option 2")
def get_all_required(self):
"""would return a list of option functions within the class that have their decorator required flag set to true"""
pass
def get_all_available(self):
"""would return all options regardless of required flag set"""
pass
def print_all_functions(self):
"""would call str(option_1) and print {} - {} for example"""
pass
a = OptionClass()
print(a.option_1("test")) # TypeError: option1() missing 1 required positional argument: 'test_text'
print(a.option_1(a,"test")) #Prints: option test
Problem 1
You implemented the method wrapper as a custom callable instead of as a normal function object. This means that you must implement the __get__() descriptor that transforms a function into a method yourself. (If you had used a function this would already be present.)
from types import MethodType
class Dec:
def __init__(self, f):
self.f = f
def __call__(self, *a, **kw):
return self.f(*a, **kw)
def __get__(self, obj, objtype=None):
return self if obj is None else MethodType(self, obj)
class Foo:
#Dec
def opt1(self, text):
return 'foo' + text
>>> Foo().opt1('two')
'footwo'
See the Descriptor HowTo Guide
Problem 2
The callable option_decorator instance replaces the function in the OptionClass dict. That means that mutating the callable instance affects all instances of OptionClass that use that callable object. Make sure that's what you want to do, because if you want to customize the methods per-instance, you'll have to build this differently.
You could access it in class definition like
class OptionClass(object):
"""defines a bunch of options"""
#option_decorator
def option_1(self,test_text):
return("option {}".format(test_text))
option_1.set_name('foo')
Problem 3
The __call__ method in your example isn't returning a function. It's returning the result of the function_ptr invocation. But that will be a coroutine object if you define your options using async def, which you would have to do anyway if you're using the async/await syntax in the function body. This is similar to the way that yield transforms a function into a function that returns a generator object.

get return value from function in a class in variable python [duplicate]

If I have a class ...
class MyClass:
def method(arg):
print(arg)
... which I use to create an object ...
my_object = MyClass()
... on which I call method("foo") like so ...
>>> my_object.method("foo")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: method() takes exactly 1 positional argument (2 given)
... why does Python tell me I gave it two arguments, when I only gave one?
In Python, this:
my_object.method("foo")
... is syntactic sugar, which the interpreter translates behind the scenes into:
MyClass.method(my_object, "foo")
... which, as you can see, does indeed have two arguments - it's just that the first one is implicit, from the point of view of the caller.
This is because most methods do some work with the object they're called on, so there needs to be some way for that object to be referred to inside the method. By convention, this first argument is called self inside the method definition:
class MyNewClass:
def method(self, arg):
print(self)
print(arg)
If you call method("foo") on an instance of MyNewClass, it works as expected:
>>> my_new_object = MyNewClass()
>>> my_new_object.method("foo")
<__main__.MyNewClass object at 0x29045d0>
foo
Occasionally (but not often), you really don't care about the object that your method is bound to, and in that circumstance, you can decorate the method with the builtin staticmethod() function to say so:
class MyOtherClass:
#staticmethod
def method(arg):
print(arg)
... in which case you don't need to add a self argument to the method definition, and it still works:
>>> my_other_object = MyOtherClass()
>>> my_other_object.method("foo")
foo
In simple words
In Python you should add self as the first parameter to all defined methods in classes:
class MyClass:
def method(self, arg):
print(arg)
Then you can use your method according to your intuition:
>>> my_object = MyClass()
>>> my_object.method("foo")
foo
For a better understanding, you can also read the answers to this question: What is the purpose of self?
Something else to consider when this type of error is encountered:
I was running into this error message and found this post helpful. Turns out in my case I had overridden an __init__() where there was object inheritance.
The inherited example is rather long, so I'll skip to a more simple example that doesn't use inheritance:
class MyBadInitClass:
def ___init__(self, name):
self.name = name
def name_foo(self, arg):
print(self)
print(arg)
print("My name is", self.name)
class MyNewClass:
def new_foo(self, arg):
print(self)
print(arg)
my_new_object = MyNewClass()
my_new_object.new_foo("NewFoo")
my_bad_init_object = MyBadInitClass(name="Test Name")
my_bad_init_object.name_foo("name foo")
Result is:
<__main__.MyNewClass object at 0x033C48D0>
NewFoo
Traceback (most recent call last):
File "C:/Users/Orange/PycharmProjects/Chapter9/bad_init_example.py", line 41, in <module>
my_bad_init_object = MyBadInitClass(name="Test Name")
TypeError: object() takes no parameters
PyCharm didn't catch this typo. Nor did Notepad++ (other editors/IDE's might).
Granted, this is a "takes no parameters" TypeError, it isn't much different than "got two" when expecting one, in terms of object initialization in Python.
Addressing the topic: An overloading initializer will be used if syntactically correct, but if not it will be ignored and the built-in used instead. The object won't expect/handle this and the error is thrown.
In the case of the sytax error: The fix is simple, just edit the custom init statement:
def __init__(self, name):
self.name = name
Newcomer to Python, I had this issue when I was using the Python's ** feature in a wrong way. Trying to call this definition from somewhere:
def create_properties_frame(self, parent, **kwargs):
using a call without a double star was causing the problem:
self.create_properties_frame(frame, kw_gsp)
TypeError: create_properties_frame() takes 2 positional arguments but 3 were given
The solution is to add ** to the argument:
self.create_properties_frame(frame, **kw_gsp)
As mentioned in other answers - when you use an instance method you need to pass self as the first argument - this is the source of the error.
With addition to that,it is important to understand that only instance methods take self as the first argument in order to refer to the instance.
In case the method is Static you don't pass self, but a cls argument instead (or class_).
Please see an example below.
class City:
country = "USA" # This is a class level attribute which will be shared across all instances (and not created PER instance)
def __init__(self, name, location, population):
self.name = name
self.location = location
self.population = population
# This is an instance method which takes self as the first argument to refer to the instance
def print_population(self, some_nice_sentence_prefix):
print(some_nice_sentence_prefix +" In " +self.name + " lives " +self.population + " people!")
# This is a static (class) method which is marked with the #classmethod attribute
# All class methods must take a class argument as first param. The convention is to name is "cls" but class_ is also ok
#classmethod
def change_country(cls, new_country):
cls.country = new_country
Some tests just to make things more clear:
# Populate objects
city1 = City("New York", "East", "18,804,000")
city2 = City("Los Angeles", "West", "10,118,800")
#1) Use the instance method: No need to pass "self" - it is passed as the city1 instance
city1.print_population("Did You Know?") # Prints: Did You Know? In New York lives 18,804,000 people!
#2.A) Use the static method in the object
city2.change_country("Canada")
#2.B) Will be reflected in all objects
print("city1.country=",city1.country) # Prints Canada
print("city2.country=",city2.country) # Prints Canada
It occurs when you don't specify the no of parameters the __init__() or any other method looking for.
For example:
class Dog:
def __init__(self):
print("IN INIT METHOD")
def __unicode__(self,):
print("IN UNICODE METHOD")
def __str__(self):
print("IN STR METHOD")
obj = Dog("JIMMY", 1, 2, 3, "WOOF")
When you run the above programme, it gives you an error like that:
TypeError: __init__() takes 1 positional argument but 6 were given
How we can get rid of this thing?
Just pass the parameters, what __init__() method looking for
class Dog:
def __init__(self, dogname, dob_d, dob_m, dob_y, dogSpeakText):
self.name_of_dog = dogname
self.date_of_birth = dob_d
self.month_of_birth = dob_m
self.year_of_birth = dob_y
self.sound_it_make = dogSpeakText
def __unicode__(self, ):
print("IN UNICODE METHOD")
def __str__(self):
print("IN STR METHOD")
obj = Dog("JIMMY", 1, 2, 3, "WOOF")
print(id(obj))
If you want to call method without creating object, you can change method to static method.
class MyClass:
#staticmethod
def method(arg):
print(arg)
MyClass.method("i am a static method")
I get this error when I'm sleep-deprived, and create a class using def instead of class:
def MyClass():
def __init__(self, x):
self.x = x
a = MyClass(3)
-> TypeError: MyClass() takes 0 positional arguments but 1 was given
You should actually create a class:
class accum:
def __init__(self):
self.acc = 0
def accumulator(self, var2add, end):
if not end:
self.acc+=var2add
return self.acc
In my case, I forgot to add the ()
I was calling the method like this
obj = className.myMethod
But it should be is like this
obj = className.myMethod()

No attribute error even when the attribute is set

So I have a class that extends two classes deep, here is it's definition and __init__():
class ProspectEventSocketProtocol(ChannelEventSocketProtocol):
def __init__(self, *args, **kwargs):
super(ProspectEventSocketProtocol, self).__init__(*args, **kwargs)
self.channel_info = None
self.rep_uuid = None
self.manual_dial = None
self.datetime_setup = timezone.now()
self.datetime_answered = None
self.defer_until_answered = defer.Deferred()
self.defer_until_originated = defer.Deferred()
self.defer_until_finished = defer.Deferred()
The definition and __init__() for the ChannelEventSocketProtocol is here:
class ChannelEventSocketProtocol(Freeswitch):
def __init__(self, *args, **kwargs):
self.channel_driver = None
self.uuid = kwargs.pop('uuid', str(uuid4()))
self._call_driver = kwargs.pop('call_driver', None)
super(ChannelEventSocketProtocol, self).__init__(*args, **kwargs)
And the definition and __init__() for the Freeswitch class is here:
class Freeswitch(client.EventSocketProtocol, TwistedLoggingMixin):
def __init__(self, *args, **kwargs):
self.jobs = {}
self.defer_until_authenticated = defer.Deferred() # This is the problem
client.EventSocketProtocol.__init__(self, *args, **kwargs)
TwistedLoggingMixin.__init__(self)
Even though I know that this is running and the defer_until_authenticated is being set as well as it's callback and errback, when I call this:
live_call = yield self._create_client_dial_live_call(client_dial.cid, client_dial.campaign)
pchannel = yield self.realm.get_or_create_channel_driver(live_call.uuid, 'prospect')
# ...
client_dial.prospect_channel = pchannel
yield pchannel.freeswitch_protocol.defer_until_authenticated # This is the problem here!
I get the error:
type object 'ProspectEventSocketProtocol' has no attribute 'defer_until_authenticated'
I have no idea why I can't get the attribute again. I know it is being set, but I have no idea where it goes... or what happens to it. I've searched the error and I have no idea what is happening in this spot.
Just for reference, here are the _create_client_dial_live_call() and get_or_create_channel_driver() functions:
def _create_client_dial_live_call():
# ...
p, created = Prospect.objects.get_or_create_client_dial_prospect(campaign, cid_num)
# ...
live_call = LiveCall(prospect=p, campaign=campaign.slug)
live_call.channel_vars_dict = chan_vars
live_call.save()
# ...
def get_or_create_channel_driver()
# The code is kind of confusing with even more context,
# it basically either gets the existing ProspectChannel
# object or creates a new one and then returns it.
Something somewhere is forgetting to instantiate a class.
The error is not telling you that an instance of the class ProspectEventSocketProtocol has no attribute defer_until_authenticated. It's telling you that the class ProspectEventSocketProtocol itself has no such attribute.
In other words, you are quite probably writing something like
pchannel.freeswitch_protocol = ProspectEventSocketProtocol
when you want
pchannel.freeswitch_protocol = ProspectEventSocketProtocol(...)
instead.
Here's a quick demo script that reproduces the error message you are seeing:
#!/usr/bin/env python3
class Test(object):
def __init__(self):
self.arg = "1234"
correct = Test()
print(correct.arg)
wrong = Test
print(wrong.arg)
When I run it, I get the following output:
1234
Traceback (most recent call last):
File "./type_object_error.py", line 12, in <module>
print(wrong.arg)
AttributeError: type object 'Test' has no attribute 'arg'

Overriding decorated subclass methods

I'm fiddling around with inheritance and found a behavior that seems strange to me---namely, that some times I can override a parent decorator function (used for validation), but sometimes I cannot, and I cannot understand why or what the difference is.
A quick walkthrough in words---I have a person object I'd like subclass to a more particular person object. The more particular one will have an additional field, "Dance," and will have different validation rules on a previous field, "name."
Here's my base case which works:
# Define the validation wrapper
def ensure(name, validate, doc=None):
def decorator(Class):
privateName = "__" + name
def getter(self):
return getattr(self, privateName)
def setter(self, value):
validate(name, value)
setattr(self, privateName, value)
setattr(Class, name, property(getter, setter, doc=doc))
return Class
return decorator
# Define the not string validation
def is_not_str(name, value):
if isinstance(value, str):
raise ValueError("{} cannot be a string.".format(name))
# Chosen to be exact opposite of above---demonstrating it's possible to reverse.
def is_str(name, value):
if not isinstance(value, str):
raise ValueError("{} must be a string.".format(name))
#ensure("name", is_str)
#ensure("url", is_str)
class Person(object):
def __init__(self,s):
self.name = s.get('name',{})
self.url = s.get('url','')
def __str__(self):
return "Person({{'name':'{}','url':'{}'}})".format(self.name, self.url)
def __repr__(self):
return str(self)
#ensure("name", is_not_str) # require a number rather than a Name() object.
class Crazyperson(Person):
def __init__(self,s):
super(Crazyperson,self).__init__(s) # idiom to inherit init
self.dance = s.get('dance') # add new param.
bill = Person({"name":"bill",
"url":"http://www.example.com"})
fred = Crazyperson({"name":1,
"url":"http://www.example.com",
"dance":"Flamenco"})
This works fine. So, the first object, bill, is created in such a way that the validation is_str succeeds. If you try to put a number there, it fails. The second object, likewise, accepts non-strings, so fred is created successfully.
Now, here's the case where it breaks, which I'd like to understand...
def is_Name(name, value):
if not isinstance(value, dict) and not isinstance(value,Name):
raise ValueError("{} must be a valid Name object".format(name))
# new object that will be a non-string type of name.
#ensure("firstname", is_str)
#ensure("lastname", is_str)
class Name(object):
def __init__(self,s):
self.firstname = s.get('firstname','')
self.lastname = s.get('lastname')
def __str__(self):
return "Name({{'firstname':'{}','lastname':'{}' }})".format(self.firstname, self.lastname)
def __repr__(self):
return str(self)
#ensure("name", is_Name) # require it as the default for the base class
#ensure("url", is_str)
class Person(object):
def __init__(self,s):
self.name = Name(s.get('name',{}))
self.url = s.get('url','')
def __str__(self):
return "Person({{'name':'{}','url':'{}'}})".format(self.name, self.url)
def __repr__(self):
return str(self)
#ensure("name", is_str) # require a number rather than a Name() object.
class Crazyperson(Person):
def __init__(self,s):
super(Crazyperson,self).__init__(s)
self.name = s.get('name','') # THIS IS THE KEY
self.dance = s.get('dance')
bill = Person({"name":{"firstname":"Bill", "lastname":"billbertson"},
"url":"http://www.example.com"})
fred = Crazyperson({"name":"Fred",
"url":"http://www.example.com",
"dance":"Flamenco"})
In this instance, the Crazyperson fails. The error suggests that the is_Name validation function in the __init__ is still being applied:
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
File "<stdin>", line 4, in __init__
File "<stdin>", line 5, in __init__
File "<stdin>", line 5, in __init__
AttributeError: 'str' object has no attribute 'get'
It looks like it has called the Name initializer: Name(s.get('name',{})) on the string name "Fred".
But it seems it can't be, because in the previous example, I was able to remove a completely contradictory validation (is_str versus is_not_str). Why is this less opposite but failing more? In the first case it wasn't applying both is_str and is_not_str, why is it /now/ applying both is_Name and is_str with seemingly identical syntax?
My question is: what's different about the first way of doing this that causes it to succeed from the second way? I've tried to isolate variables here, but don't understand why I can undo the wrapped validator inherited from the parent class in Scenario I but cannot do what seems similar in Scenario II. It seems the only meaningful difference is that it's an object instead of a string.
(I understand that the better architectural way to do this would be to have a third more abstract parent class, with no validation rules that need changing---and both kinds of person would inherit from that. But I also understand I am supposed to be able to change methods in subclasses, so I'd like to at least understand the difference between why one is succeeding and the other failing here.)
In your second setup, the is_Name function is not applied. You are creating Name object, regardless, in the __init__ method:
class Person(object):
def __init__(self,s):
self.name = Name(s.get('name',{}))
self.url = s.get('url','')
Note the self.name = Name(...) line there.
In Crazyperson.__init__() you call the parent method:
def __init__(self,s):
super(Crazyperson,self).__init__(s)
self.dance = s.get('dance')
passing on s to Person.__init__() which creates a Name() object.
So when you create fred with fred = Crazyperson({"name":"Fred", ...}) you are passing name set to the string 'Fred' to Name.__init__(), which expected a dictionary instead:
class Name(object):
def __init__(self,s):
self.firstname = s.get('firstname','')
self.lastname = s.get('lastname')
and this is where your code fails:
>>> 'Fred'.get('firstname', '')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'get'
Only set name on Person if no self.name has been set yet:
class Person(object):
def __init__(self,s):
if not hasattr(self, 'name')
self.name = Name(s.get('name', {}))
self.url = s.get('url','')
and set name first in Crazyperson:
def __init__(self,s):
self.name = s.get('name', 0)
self.dance = s.get('dance')
super(Crazyperson,self).__init__(s)

Categories