I'm trying to write two classes with the same name in one file. Something like this:
class A:
def __init__(self, name):
self.name=name
class A:
def __init__(self, id, name,pass):
self.id=id
self.name=name
self.type=pass
and use here:
obj1=A(name)
obj2=A(id,name,pass)
is it possible?
In short, no. But:
If you have the same functionality, just want to have different constructors, use default args:
class A:
def __init__(self, name, id=None, pass=None):
self.id=id
self.name=name
self.type=pass
# These both work
obj1=A(name)
obj2=A(id,name,pass)
If you have different functionality: Use different names.
You can't. A class declaration is just a declaration, so the second one will overwrite the first, just like doing x=1; x=2. What you probably want is alternative constructors for your class. See classmethod.
You could make use the kwargs like,
$ cat kwargs.py
class A:
def __init__(self, *args, **kwargs):
self.id= kwargs.get('id')
self.name = kwargs.get('name')
self.type = kwargs.get('type')
def __str__(self):
return '{}(id={}, name={}, type={})'.format(
self.__class__.__name__,
self.id,
self.name,
self.type,
)
obj1 = A(name='foo')
obj2 = A(id='id1', name='foo', type='footype')
print(obj1)
print(obj2)
Output:
$ python kwargs.py
A(id=None, name=foo, type=None)
A(id=id1, name=foo, type=footype)
Related
I'm learning simple python inheritance and I want that one of my parent class method default argument is changed conditionally to one of my subclass argument value, and I don't know if this is possible.
Here is an example of what I'd like to do:
class Parent(object):
def __init__(self, name):
self.name = name
def doSomething(self, name, strict = True):
if strict:
return self.name
else:
return name
class Child(Parent):
def __init__(self, name, **kwargs):
super(Child, self).__init__(name)
if 'changeBehavior' in kwargs:
# Here is the thing:
# Can I change the default value of strict to kwargs['changeBehavior']
# in a way that when I later call doSomething(), it will behave according
# to its new default behavior?
def doSomething(self, name, strict = kwargs['changeBehavior']):
super(Child, self).doSomething(strict = kwargs['changeBehavior'])
If this can be done in this way, is there any other method to do so?
Thanks
You can use partial.
from functools import partial
class Parent(object):
def __init__(self, name):
self.name = name
def doSomething(self, name, strict=True):
print('Got strict={}'.format(strict))
if strict:
return self.name
else:
return name
class Child(Parent):
def __init__(self, name, **kwargs):
super().__init__(name)
change_behavior = kwargs.get('changeBehavior')
if change_behavior is not None:
self.doSomething = partial(self.doSomething, strict=change_behavior)
p = Parent('name')
c = Child('name', changeBehavior=False)
p.doSomething('name')
c.doSomething('name')
outputs
Got strict=True
Got strict=False
I need to change a inherited class to another inherited class where only one of the attributes has changed
i need to "Promote" a Cashier to a Manager, the only thing that is suppose to change is the salary
both Cashier and Manager are inherited classes of Employee (where I'm not sure if I'm using the "hasattr" function the right way)
class Employee:
def __init__(self,name):
self.name=name
if(hasattr(self,'shifts')==False):
self.shifts=[]
class Manager(Employee):
def __init__(self,name,salary):
Employee.__init__(self,name)
self.salary=salary
class Cashier(Employee):
def __init__(self,name,salarey_per_hours):
Employee.__init__(self,name)
self.salery_per_hours=salarey_per_hours
def promote(self,salary):
return Manager(self.name,salary)
P.s It's my first time uploading a question
What you could do is create the addition method of your class and add self to the manager class you are returning like so:
class Employee(object):
def __init__(self, name):
self.name=name
if not hasattr(self, 'shifts'):
self.shifts = []
def __add__(self, other):
if isinstance(other, Employee):
for key, value in other.__dict__.items():
if key == 'salary':
continue
self.__setattr__(key, value)
return self
class Manager(Employee):
def __init__(self, name, salary):
super().__init__(name)
self.salary = salary
class Cashier(Employee):
def __init__(self,name,salary):
super().__init__(name)
self.salary = salary
def promote(self, salary):
manager = Manager(self.name, salary)
manager += self
return manager
cashier = Cashier('hank', 22)
cashier.shifts = [1, 2, 3, 4]
print(cashier.shifts)
promoted_cashier = cashier.promote(30)
print(promoted_cashier.shifts)
Here you make sure that everything except the "salary" is transferred to the promoted class. And since both the Manager and the Cashier are an Employee this should work nicely. I changed your code a bit to what I'm used to since there was some unusual coding with you Calling Employee in the init which I assumed you did not explicitly needed. Sorry if that was not the case.
You can change the object's class by obj.__class__ to the another class by
doing obj.__class__ = SomeClass
Beware that is can lead to strange behaviours if it is handled incorrectly.
by modifying your code
class Employee:
def __init__(self,name):
self.name=name
if(hasattr(self,'shifts')==False):
self.shifts=[]
class Manager(Employee):
def __init__(self,name,salary):
Employee.__init__(self,name)
self.salary=salary
class Cashier(Employee):
def __init__(self,name,salarey_per_hours):
Employee.__init__(self,name)
self.salery_per_hours=salarey_per_hours
def promote(self,salary):
self.__class__ = Manager
# return Manager(self.name,salary)
You can also take a look at this post changing the class of a python object (casting)
I have a parent class and several subclasses. Every subclass accepts different parameters, but all subclasses have some common parameters. I don't want to write the "common parameters" for every subclass. How can I do this?
class Parent:
def __init__(self, name):
self.name = name
class Subclass(Parent):
def __init__(self, age):
self.age = age
def do_something(self):
print(self.name)
instance = Subclass(name="Test", age=42)
instance.do_something() # 42
You can try this:
class Subclass(Parent):
def __init__(self, **kwargs):
super().__init__(kwargs['name'])
self.age = kwargs['age']
def do_something(self):
print(self.name)
And then use this just like you did in the question:
instance = Subclass(name="Test", age=42)
I use it in the following manner
You can add as many child classes as you want
class ParentClass(object):
def __init__(self,baseArgs):
self.var1=baseArgs['var1']
self.var2=baseArgs['var2']
self.var3=baseArgs['var3']
class ChildClass(ParentClass):
def __init__(self,childArgs,baseArgs):
super(ChildClass, self).__init__(baseArgs)
self.cvar1=childArgs['cvar1']
self.cvar2=childArgs['cvar2']
a=ChildClass({'cvar1':40,'cvar2':50},{'var1':10,'var2':20,'var3':30})
print(a.var1)
# 10
I have a base class, a bunch of subclasses, and for each of these subclasses, I have another set of sub-subclasses. For example:
class BaseClass(object):
def __init__(self):
with open(config.txt) as f
self.config_array = f.readlines()
class FirstOrderSubClass(BaseClass):
def __init__(self, name):
self.name = name
class SecondOrderSubClass(FirstOrderSubClass):
def __init__(self, name, version):
self.name = name
self.version = version
super(SecondOrderSubClass, self).__init__(self.name)
# needed to access self.config_array
print self.config_array
I need to get the __init__() method of the SecondOrderSubClass to make the following assignment: self.lines = self.config_array.
EDIT: added line print self.config_array. If I run the code I get:
TypeError: __getattr__() takes exactly 1 argument (2 given)
You cannot access self.config_array until BaseClass.__init__() has run to set the attribute.
Either fix FirstOrderSubClass to also invoke the base class __init__ or call it directly.
Fixing the FirstOrderSubClass is probably the best way to do so:
class FirstOrderSubClass(BaseClass):
def __init__(self, name):
super(FirstOrderSubClass, self).__init__()
self.name = name
However, your __init__ method signatures do not match so you cannot rely on cooperative behaviour here; as soon as you add a mix-in class in the hierarchy, things can and probably will break. See *Python's super() is considered super! by Raymond Hettinger, or it's followup PyCon presentation to explain why you want your signatures to match.
Calling the BaseClass.__init__ unbound method directly (passing in self explicitly) would also work:
class SecondOrderSubClass(FirstOrderSubClass):
def __init__(self, name, version):
super(SecondOrderSubClass, self).__init__(name)
self.version = version
BaseClass.__init__(self)
Note that there is no point in assigning to self.name there if you are going to ask FirstOrderSubClass.__init__ to do the exact same thing.
The proper way to use super() is for all your methods to at least accept all the same arguments. Since object.__init__() never does, this means you need a sentinel class that does not use super(); BaseClass will do nicely here. You can use *args and **kw to capture any additional arguments and just ignore those to make cooperative subclassing work:
class BaseClass(object):
def __init__(self, *args, **kw):
with open(config.txt) as f
self.config_array = f.readlines()
class FirstOrderSubClass(BaseClass):
def __init__(self, name, *args, **kw):
super(FirstOrderSubClass, self).__init__(*args, **kw)
self.name = name
class SecondOrderSubClass(FirstOrderSubClass):
def __init__(self, name, version, *args, **kw):
super(SecondOrderSubClass, self).__init__(name, *args, **kw)
self.version = version
You have to call the FirstOrderSubClass super method:
class BaseClass(object):
def __init__(self):
with open("config.example.txt",'w') as f:
f.write("Hello world")
with open("config.example.txt") as f:
self.config_array = f.readlines()
class FirstOrderSubClass(BaseClass):
def __init__(self, name):
super(FirstOrderSubClass,self).__init__()
self.name = name
class SecondOrderSubClass(FirstOrderSubClass):
def __init__(self, name, version):
self.name = name
self.version = version
super(SecondOrderSubClass, self).__init__(self.name)
# needed to access self.config_array
grandchild = SecondOrderSubClass("peter",2.0)
print grandchild.config_array
##>>>
##['Hello world']
I have a Category class which has different names for each categories, the names of the categories can be unknown, good and bad, all categories share the same behavior so i don't want to create sub classes for each type of category, the problem comes when i am trying to
create the different categories in this way:
Category.GOOD
This statement should return a category object with his name setting to 'good' so i try
the following:
class Category(object):
def __init__(self, name):
self.name = name
#property
def GOOD(self):
category = Category(name='good')
return category
#property
def BAD(self):
category = Category(name='bad')
return category
Then i created and use the category with the following output:
c = Category.GOOD
c.name
AttributeError: 'property' object has no attribute 'name'
Realizing that this doesn't work i try a java like approach:
class Category(object):
GOOD = Category(name='good')
BAD = Category(name='bad')
def __init__(self, name):
self.name = name
What i get here is a undefined name "Category" error, so my question is if there is a pythonic way to create a category object like this.
You probably want to use classmethods:
class Category(object):
#classmethod
def GOOD(cls):
category = cls(name='GOOD')
return category
Now you can do c = Category.GOOD().
You cannot do this with a property; you either have to use a classmethod, or create your own descriptor for that:
class classproperty(property):
def __get__(self, inst, cls):
return self.fget(cls)
I'm abusing the property decorator here; it implements __set__ and __del__ as well, but we can just ignore those here for convenience sake.
Then use that instead of property:
class Category(object):
def __init__(self, name):
self.name = name
#classproperty
def GOOD(cls):
return cls(name='good')
#classproperty
def BAD(cls):
return cls(name='bad')
Now accessing Category.GOOD works:
>>> Category.GOOD
<__main__.Category object at 0x10f49df50>
>>> Category.GOOD.name
'good'
I'd use module variables for this. Consider you have the module category.py:
class Category(object):
# stuff...
now you put the two global objects in it:
GOOD = Category(name='good')
BAD = Category(name='bad')
You can use it like that:
from path.to.category import GOOD, BAD
I don't say that this is pythonic but I think this approach is elegant.
The main point that you could not use class definition inside that class definition itself. So the most straight way to achieve what you are want is to use class/static methods as shown below, or even package constants.
class Category(object):
def __init__(self, name):
self.name = name
#classmethod
def GOOD(cls):
return Category(name='good')
#classmethod
def BAD(cls):
return Category(name='bad')
print Category.GOOD().name
or
class Category(object):
def __init__(self, name):
self.name = name
#staticmethod
def GOOD():
return Category(name='good')
#staticmethod
def BAD():
return Category(name='bad')
print Category.GOOD().name