Creating objects from static properties in python - python

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

Related

What is an alternative to using `__getattr__()` method for wrapper classes?

Suppose that I have two classes:
a class named Swimmer
a class named Person
For my particular application, we can NOT have Swimmer inherit from Person, although we want something like inheritance.
Instead of class inheritance each Swimmer will have an instance of the Person class as a member variable.
class Person:
pass
class Swimmer:
def __init__(self, person):
self._person = person
def __getattr__(self, attrname:str):
try:
attr = getattr(self._person)
return attr
except AttributeError:
raise AttributeError
Perhaps the Person class has the following class methods:
kneel()
crawl()
walk()
lean_over()
lay_down()
The Swimmer class has all of the same methods as the Person class, plus some additional methods:
run()
swim()
dive()
throw_ball()
When it comes to kneeling, crawling, walking, and laying down, a Swimmer is meant to be a transparent wrapper around the Person class.
I want to write something like this:
swimmer_instance = SwimmerClass(person_instance)
I wrote a __getattr__() method.
However, I ran into many headaches with __getattr__().
Consider writing the code self.oops. There is no attribute of the _Swimmer class named oops. We should not look for oops inside of self._person.
Aanytime that I mistyped the name of an attribute of Swimmer, my computer searched for that attribute in the instance of the Person class. Normally, fixing such spelling mistakes is easy. But, with a __getattr__() method, tracking down the problem becomes difficult.
How can I avoid this problem?
Perhaps one option is create a sub-class of the Swimmer class. In the sub-class have have a method, the name of which is a misspelling of __getattr__. However, I am not sure about this idea; please advise me.
class _Swimmer:
def __init__(self, person):
self._person = person
def run(self):
return "I ran"
def swim(self):
return "I swam"
def dive(self):
# SHOULD NOT LOOK FOR `oops` inside of self._person!
self.oops
return "I dove"
def _getattrimp(self, attrname:str):
# MISSPELLING OF `__getattr__`
try:
attr = getattr(self._person)
return attr
except AttributeError:
raise AttributeError
class Swimmer(_Swimmer):
def __getattr__(self, attrname:str):
attr = self._getattrimp(attrname)
return attr
Really, it is important to me that we not look inside of self._person for anything except the following:
Kneel()
Crawl()
Walk()
Lean()
LayDown()
The solution must be more general than just something what works for the Swimmer class and Person class.
How do we write a function which accepts any class as input and pops out a class which has methods of the same name as the input class?
We can get a list of Person attributes by writing person_attributes = dir(Person).
Is it appropriate to dynamically create a sub-class of Swimmer which takes Person as input?
class Person:
def kneel(self, *args, **kwargs):
return "I KNEELED"
def crawl(self, *args, **kwargs):
return "I crawled"
def walk(self, *args, **kwargs):
return "I WALKED"
def lean_over(self, *args, **kwargs):
return "I leaned over"
################################################################
import functools
class TransparentMethod:
def __init__(self, mthd):
self._mthd = mthd
#classmethod
def make_transparent_method(cls, old_method):
new_method = cls(old_method)
new_method = functools.wraps(old_method)
return new_method
def __call__(self, *args, **kwargs):
ret_val = self._mthd(*args, **kwargs)
return ret_val
###############################################################
attributes = dict.fromkeys(dir(Person))
for attr_name in attributes.keys():
old_attr = getattr(Person, attr_name)
new_attr = TransparentMethod.make_transparent_method(old_attr)
name = "_Swimmer"
bases = (object, )
_Swimmer = type(name, bases, attributes)
  
class Swimmer(_Swimmer):
pass
If I understand your question correctly, you want a function that will combine two classes into one.
The way I did this was to create a blank container class with the 3 parameter type() constructor, then loop over every class passed to the function, using setattr to set new attributes of the container class. I had to blacklist the __class__ and __dict__ attributes because Python doesn't allow one to change these. Note that this function will overwrite previously added methods, such as the __init__() method, so pass the class with the constructor last.
I implemented this in the combineClasses function below. I also provided an example. In the example, I created the a basic Person class and a _Swimmer class. I called combineClasses on these two and stored the resulting class as Swimmer, so it can nicely be called as a wrapper class.
def combineClasses(name, *args):
container = type(name, (object,), {})
reserved = ['__class__', '__dict__']
for arg in args:
for method in dir(arg):
if method not in reserved:
setattr(container, method, getattr(arg, method))
return container
class Person:
def __init__(self, name):
self.name = name
def sayHi(self):
print(f'Hi, I am {self.name}')
class _Swimmer:
def swim(self):
print('I am swimming')
class _Cashier:
def work(self):
print(f'I am working! My name is {self.name}')
Swimmer = combineClasses('Swimmer', _Swimmer, Person)
bob = Swimmer('Bob')
bob.swim() # => "I am swimming"
bob.sayHi() # => "Hi, I am Bob"
print(bob.name) # => "Bob"
print(type(bob)) # => "<class '__main__.Swimmer'>"

Calling class methods from class body

I have the code something like:
class ClassPrintable:
#classmethod
def print_class(cls):
print(cls)
I would like to be able to derive classes from this, and furthermore call the class methods inline from the class body, eg.
class MyClass(ClassPrintable):
print_class()
Unfortunately this doesn't work, however this does:
class MyClass(ClassPrintable):
ClassPrintable.print_class()
Unfortunately, of course, it prints the class for ClassPrintable rather than MyClass
The obvious solution, doesn't work, eg.
class MyClass(ClassPrintable):
MyClass.print_class()
Python complains it can't find MyClass! with a NameError: name 'MyClass' is not defined
How can I access MyClass's class method from within the body of its definition? I would prefer not to use dynanic metaprogramming but I will if I have to.
You cannot invoke anything on the class before it exists which is only after the class definition (note that method bodies aren't evaluated at class definition time). In Python >= 3.6, you can do the following, using the __init_subclass__ hook:
class ClassPrintable:
#classmethod
def print_class(cls):
print(cls)
#classmethod
def __init_subclass__(cls):
cls.print_class()
class MyClass(ClassPrintable):
pass
Alright I figured it out with small amount of metaprogramming. Whoever thought of __init_subclass__ is a genius. If anyone can see anything drastically wrong with this let me know.
import copy
class Model:
def __init__(self, name, default):
self.model_name = name
self.model_default = default
self.observers = []
class Models():
model_dictionary = {}
def __init_subclass__(cls, models=[]):
setattr(cls, "model_dictionary", {})
for model in models:
cls.model_dictionary[model[0]] = Model(model[0], model[1])
for c in cls.__bases__:
cls.add_base_models(c)
#classmethod
def add_base_models(cls, base):
if hasattr(base, "model_dictionary"):
for model in base.model_dictionary.values():
cls.model_dictionary[model.model_name] = copy.copy(base.model_dictionary[model.model_name])
for c in base.__bases__:
cls.add_base_models(c)
#classmethod
def listen(cls, name, closure):
cls.model_dictionary[name].observers.append(closure)
def __init__(self):
for model in self.model_dictionary.values():
super().__setattr__(model.model_name, model.model_default)
def __setattr__(self, name, value):
if name in self.__class__.model_dictionary.keys():
orig_value = getattr(self, name)
if value != orig_value:
for observer in self.model_dictionary[name].observers:
observer(self, value)
super().__setattr__(name, value)
else:
super().__setattr__(name, value)
Sample use of the code:
class Mouse(Models, models=[("x", 100), ("y", 200), ("visible", True)]):
pass
class SpecialMouse(Mouse, models=[("anger_level", "hostile")]):
pass
mouse = SpecialMouse()
mouse.listen("anger_level", lambda mouse, value : print(value))
mouse.anger_level = "cold!"
mouse.anger_level = "warm"
mouse.anger_level = "warm"
mouse.anger_level = "furious"
Prints out:
cold!
warm
furious

How to write two same name classes

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)

Python: Using properties of an outer class

I have some code that looks like this:
class Log(object):
#property
def log(self):
return self.log
class ExampleClass2(ExampleClass, Log):
class ExampleClass3(object):
#property
def log_value(self):
self.log.info('Hi!')
However I'm getting an error,
'ExampleClass3' object has not attribute 'log'
I'm guessing I need to add an __init__() method to DEF, and I've tried using
super(ExampleClass2.ExampleClass3, self).__init__()
but I'm still having problems accessing log. Any suggestions?
I believe to get your desired behavior, you need need to pass in an instance of ExampleClass2 when you create an instance of ExampleClass3.
class OuterClass:
def __init__(self, value):
self.value = value
class InnerClass:
def __init__(self, instance):
self.instance = instance
def inner_print_value(self):
print self.instance.value
def outer_print_value(self):
printer = OuterClass.InnerClass(self)
printer.inner_print_value()
OuterClass('Hi').outer_print_value() # 'Hi'
As noted in the comments, there is rarely a reason for this kind of structure. It would be easier to create InnerClass outside of the definition of OuterClass.
class OuterClass:
def __init__(self, value):
self.value = value
def outer_print_value(self):
printer = InnerClass(self)
printer.inner_print_value()
class InnerClass:
def __init__(self, instance):
self.instance = instance
def inner_print_value(self):
print self.instance.value
It seems like you're expecting the value of self to be augmented when creating an inner-class, but this is not the case. To do this, you'd want to use inheritance, and that doesn't require nested classes either.

Access the class type when constructing a class attribute in python?

I'd like to do this:
class MyThing(object):
def __init__(self,owning_cls):
self.owning_cls = owning_cls
class MyClass(object):
thing = MyThing(self.__class__)
print MyClass.thing.owning_cls
This doesn't work - as there isn't a self to refer to in the class construction of MyClass.
Is there any way to achieve this (it's clearly trivial if we make thing an instance attribute, but I'd like to be a class attribute please!)?
Perform the call immediately after the class declaration:
class MyClass(object): pass
MyClass.thing = MyThing(MyClass)
Use a decorator. I find this to be a clean solution because it lets you keep more of the class definition together, rather than having to write additional class-related code after the class definition or forcing you to instantiate MyClass, etc.
class MyThing(object):
def __init__(self,owning_cls):
self.owning_cls = owning_cls
def set_thing(cls):
cls.thing = MyThing(cls)
return cls
#set_thing
class MyClass(object):
pass
>>> print MyClass.thing.owner_cls
<class '__main__.MyClass'>
Maybe you can initialize the class with __new__?
Use desciptor:
class Ownable(object):
def __init__(self, clz):
self._clz = clz
self._inst = None
def __get__(self, inst, owner_clz):
self._inst = self._inst or self._clz(owner_clz)
return self._inst
class MyThing(object):
def __init__(self, owner_clz):
self.owner_clz = owner_clz
class MyClass(object):
thing = Ownable(MyThing)
>>> print MyClass.thing.owner_clz
<class '__main__.MyClass'>
Ah, the use MetaClasses comment helps a lot here.
This looks like an "easy" way to achieve exactly what I want
class MyClassMetaclass(type):
def __new__(cls, name, bases, dct):
cls.thing = MyThing(name)
return super(MyClassMetaclass, cls).__new__(cls, name, bases, dct)
class MyThing(object):
def __init__(self,owning_cls):
self.owning_cls = owning_cls
class MyClass(object):
__metaclass__=MyClassMetaclass
print MyClass.thing.owning_cls

Categories