I have a class property that I want to populate at runtime, but I don't want to pass the value. What I am doing right now is:
weird_class.py
class WeirdClass:
prop = NotImplementedError
runtime_file.py
from weird_class import WeirdClass
PROP = 'somevalue'
class WeirdClassChild(WeirdClass):
prop = PROP
This works, but whenever I create a WeirdClassChild, I have to set the prop value, which is irritating.
What I would like is for WeirdClassChild to automatically pick up the PROP global variable without me specifically telling it to.
I would like to do something like:
class WeirdClass:
prop = __global_namespace__.PROP
Is this possible in python?
You can use inspection to determine the context from which your class is instantiated:
# class definition
import inspect
class Weird:
def __init__(self):
caller_frame = inspect.stack()[1]
caller_module = caller_frame[0]
self.prop = caller_module.f_globals['PROP']
# instantiation
PROP = 555
x = Weird()
print(x.prop)
=> 555
I wouldn't necessarily recommend it, but if you have a good reason to do this...
You may be able to use metaclasses:
#!/usr/bin/env python3
PROP = "only for subclasses"
class _WierdMeta(type):
# Not sure if you should use __init__ or __new__
# Use one or the other.
# `cls` is an instance of the class type that _WierdMeta creates
def __init__(cls, name, bases, dct):
if bases:
cls.prop = PROP
super().__init__(name, bases, dct)
# `cls` is _WierdMeta
def __new__(cls, name, bases, dct):
class_type = super().__new__(cls, name, bases, dct)
if bases:
class_type.prop = PROP
# this return value will be the `cls` passed to __init__ above
return class_type
class WierdBase(metaclass=_WierdMeta):
"""Base class."""
prop = "base"
class WierdChild(WierdBase):
pass
wb = WierdBase()
wc = WierdChild()
print(wb.prop) # print('base')
print(wc.prop) # print('only for subclasses')
It appears that as of Python 3.6, you can do it using __init_subclass__.
class WierdBase():
"""Base class."""
prop = "base"
# I'm not 100% on the args here...
def __init_subclass__(cls, **kwargs):
# ... or here.
super().__init_subclass__(**kwargs)
if cls is not WierdBase:
cls.prop = PROP
Based on your last comment of how you use this, why not add another base class?
from weird_class import WeirdClass
PROP = 'somevalue'
class WeirdClassChildBase(WeirdClass):
prop = PROP
class WeirdClassChild_1(WeirdClassChildBase):
pass
class WeirdClassChild_2(WeirdClassChildBase):
pass
...
I would split this into three files and follow the approach explained here: https://instructobit.com/tutorial/108/How-to-share-global-variables-between-files-in-Python :
globals.py - here you initialize the value of the PROP
weird_class.py - here you should import the globals.py and use the PROP value
start.py - here you can test the weird class, but before importing its module you should import globals.py and call some intialization method
The globals.py file will agregate all the global default values. Assuming in a start.py script first you should import globals and initialize it.
import globals
globals.initialize()
from weird_class import WeirdClass
c = WeirdClass()
print(c.prop)
In your weird_class file you can access the variable from globals.py provided you have imported it:
import globals
class WeirdClass:
prop = globals.PROP
and finally the globals.py can look like:
def initialize():
global PROP
PROP = "my global value"
The key part here is to declare global PROP before setting an initial value. The global keyword will make the variable global in the module globals.
Having global variables, constants and all magic values in a single, central place scoped with the module is often an advantage.
Related
I need to get class name from class:
class Cls:
notation = None
def __init__(self):
notation = self.__class__.__name__
print(Cls.notation) prints None but I need 'Cls'
How to fix it or how to define class attribute which returns a name of class?
You are assigning to a local variable, not the class attribute:
def __init__(self):
Cls.notation = self.__class__.__name__
Note that self.__class__ isn't necessarily Cls, if there is a subclass of Cls involved. You might want to use
def __init__(self):
type(self).notation = self.__class__.__name__
depending on your use case.
Assigning to self.notation won't work, because that creates an instance attribute that shadows the class attribute.
If you want Cls.notation == "Cls" immediately after the class is defined, you may as well just hard-code it:
class Cls:
notation = "Cls"
or
class Cls:
pass
Cls.notation = Cls.__name__
though you can also write
class Cls:
notation = __qualname__
to set its value based on the name used in the first line of the statement, though __qualname__ takes into account nesting as well:
class Cls1:
class Cls2:
notation = __qualname__ # "Cls1.Cls2", not "Cls2"
I'm trying to "simulate" namespacing in python. I'm using inner and outer class hirarchies to create my namespaces. For example you want to save paths of files (like resources) in one location. I tried something like this:
src = #path to source folder
class Resources:
root = src + "Resources\\"
class Fonts:
root = Resources.root + "fonts\\"
font1 = root + "font1.ttf"
font2 = root + "font2.ttf"
class Images:
root = Resources.root + "images\\"
logo = root + "logo"
image1= root + "image1"
class StyleSheets:
root = Resources.root + "stylesheets\\"
default = root + "default.qss"
class JsonData:
root = src + "Data\\"
class TableEntries:
root = JsonData.root
entries1 = root + "Entries1.json"
entries2 = root + "Entries2.json"
Accessing elements would look like this:
logoPath = Resources.Images.image1
Unfortunatly this isn't working due to the following error:
root = Resources.root + "fonts\\"
NameError: name 'Resources' is not defined
My Question
Is it possible to set class variables of inner class based on class variables of outer class? If not, is there another way to access the elements as shown above without using multiple files?
Is it possible to set class variables of inner class based on class variables of outer class?
Not without ressorting to a custom metaclass to process the inner classes, which will certainly not help readability nor maintainability (and will be - rightly - seen by any experienced python programmer as a total WTF).
EDIT : well actually for your example snippet the metaclass solution is not that complicated, cf the end of this answer
The reason is that in Python almost everything happens at runtime. class is an executable statement, and the class object is only created and bound to it's name after the end of the whole class statement's body.
If not, is there another way to access the elements as shown above without using multiple files?
Quite simply (dumbed down example):
import os
# use a single leading underscore to mark those classes
# as "private" (=> not part of the module's API)
class _Fonts(object):
def __init__(self, resource):
self.font1 = os.path.join(resource.root, "font1.ttf")
self.font2 = os.path.join(resource.root, "font2.ttf")
class _Resources(object):
def __init__(self, src):
self.root = os.path.join(rsc, "Ressources")
self.Fonts = _Fonts(self)
# then instanciate it like any other class
src = "/path/to/source/folder"
Resources = _Resources(src)
print(Resources.Fonts.font1)
EDIT : after a bit more thinking a metaclass-based solution for your use case would not be that complicated (but this will NOT be anything generic):
import os
class ResourcesMeta(type):
def __init__(cls, name, bases, attrs):
for name in attrs:
obj = getattr(cls, name)
if isinstance(obj, type) and issubclass(obj, SubResource):
instance = obj(cls)
setattr(cls, name, instance)
class SubResourceMeta(type):
def __new__(meta, name, bases, attrs):
if not bases:
# handle the case of the SubResource base class
return type.__new__(meta, name, bases, attrs)
root = attrs.pop("root")
cls = type.__new__(meta, name, bases, {})
cls._root = root
cls._attrs = attrs
return cls
class SubResource(metaclass=SubResourceMeta):
def __init__(self, parent):
self.root = os.path.join(parent.root, self._root)
for name, value in self._attrs.items():
setattr(self, name, os.path.join(self.root, value))
class Resources(metaclass=ResourcesMeta):
root = "/path/to/somewhere"
class Fonts(SubResource):
root = "fonts"
font1 = "font1.ttf"
font2 = "font2.ttf"
class Images(SubResource):
root = "images"
logo = "logo"
image1= "image1"
I think that you do not have clear the concept of class and instaces in OOP. If you want to store this kind of information Resources shoult not be a class, it should be an instance of a Dirclass.
class Dir:
def __init__(self, path="/", parent=None):
self.parent = parent
self.path = path
self.contents = {}
def __getitem__(self, key):
return self.contents[key]
def create_subdir(name):
self.contents[name] = Dir(os.path.join(self.path + name), self)
def add_file(file):
self.contents[file] = file # You should probably also have a File type
# ...
resources = Dir(os.path.join(src, "Resources"))
resources.create_subdir("fonts")
fonts = resources["fonts"]
fonts.add_file("font1.ttf")
...
I've used os.path.join function to delegate to Python choosing the correct delimiter for each SO instead of hardcoding Windows delimiters as you have. The __getitem__method allows to get items as if the variable was a dictionary directly.
EDIT:
You could take advantage of pathlib standard module and add the attribute access notation (using '.' to acces the subdirectories) if you don't like the div operator usage of pathlib.
from pathlib import Path as Path_, WindowsPath as WPath_, PosixPath as PPath_
import os
class Path(Path_):
def __new__(cls, *args, **kwargs):
return super().__new__(WindowsPath if os.name == 'nt' else PosixPath,
*args, **kwargs)
def __getattr__(self, item):
if item == '_str':
raise AttributeError
for i in self.iterdir():
if i.name == item:
return i
raise AttributeError
class WindowsPath(WPath_, Path):
pass
class PosixPath(PPath_, Path):
pass
current = Path()
subdir = current.subdir_name # current / 'subdir_name'
Currently __setattr__ only works for instance. Is there any similar method for class? I am asking this question because I want to collect the list of defined attribute in order when user define it in class as below:
class CfgObj(object):
_fields = []
def __setattr__(self, name, value):
self._fields.append([name, value])
object.__setattr__(self, name, value)
class ACfg(CfgObj):
setting1 = Field(str, default='set1', desc='setting1 ...')
setting2 = Field(int, default=5, desc='setting2...')
I know the above code will not work as expected because the __setattr__ only called by instance as below:
acfg = ACfg()
acfg.c = 1
acfg._fields == [['c', 1]]
So, is there any equivalent __setattr__ for python class? The main purpose is to collect the define attribute in order when user define it in class.
Yes, but that's not how you want to do it.
class MC(type):
def __init__(cls, name, bases, dct):
print dct
super(MC, cls).__init__(name, bases, dct)
class C(object):
__metaclass__ = MC
foo = 42
If you define __setattr__() on the metaclass of a class, it will be called when setting attributes on the class, but only after creating the class:
>>> class Meta(type):
... def __setattr__(cls, name, value):
... print "%s=%r" % (name, value)
...
>>> class A(object):
... __metaclass__ = Meta
...
>>> A.a = 1
a=1
But it won't work at the time of class definition, so it's probably not what you want.
Getting the class attributes in the metaclass __init__() works, but you loose the order of definition (and multiple definitions as well).
What I would do to solve your problem - but not your question - is to set the timestamp of the field creation create a counter of Field objects and set the current value of the counter to the created one:
class Field(object):
count = 0
def __init__(self, value, default=None, desc=None):
self.value = value
self.default = default
self.desc = desc
# Here comes the magic
self.nth = Field.count
Field.count += 1
# self.created_at = time.time()
Then I would create a method for returning all fields ordered by its counter value:
class CfgObj(object):
def params(self):
ns = dir(self)
fs = [getattr(self, field)
for field in ns
if isinstance(getattr(self, field), Field)]
# fs = sorted(fs, key=lambda f: f.created_at)
fs = sorted(fs, key=lambda f: f.nth)
return fs
Its usage is intuitive:
class ACfg(CfgObj):
setting1 = Field(str, default='set1', desc='setting1 ...')
setting2 = Field(int, default=5, desc='setting2...')
print ACfg().params()
Clearly the fields are ordered by time of object creation, not field creation, but it can be enough for you. Is it?
I'm trying to create a metaclass for the class I created here: ctypes variable length structures
I want to simplify the Points class so it looks like this (Python 3.2):
class Points(c.Structure, metaclass=VariableMeta):
_fields_ = [
('num_points', c.c_uint32),
('points', 'Point*self.num_points')
]
def __init__(self):
self.num_points = 0
self.points = [0,]*MAX_SIZE
This is the metaclass I have so far:
class VariableMeta(type):
def __new__(cls, name, bases, dct):
dct['_inner_fields'] = dct['_fields_']
dct['_fields_'] = [('_buffer', c.c_byte*MAX_PACKET_SIZE)]
return type.__new__(cls, name, bases, dct)
def parse(self):
fields = []
for name, ctype in self._inner_fields:
if type(ctype) == str:
ctype = eval(ctype)
fields.append((name, ctype))
class Inner(c.Structure, PrettyPrinter):
_fields_ = fields
inner = Inner.from_address(c.addressof(self._buffer))
setattr(self, name, getattr(inner, name))
self = inner
return self
def pack(self):
fields = []
for name, ctype in self._inner_fields:
if type(ctype) == str:
ctype = eval(ctype)
fields.append((name, ctype))
class Inner(c.Structure, PrettyPrinter):
_fields_ = fields
inner = Inner()
for name, ctype in self._inner_fields:
value = getattr(self, name)
if type(value) == list:
l = getattr(inner, name)
for i in range(len(l)):
l[i] = getattr(self, name)[i]
else:
setattr(inner, name, value)
return inner
It looks like it should work, but when I run it I get the error: TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases.
I searched for hints to the solution of this problem, but ctypes Structure looks to be implemented in a c library. I am not sure how to fix this, any help or the specific solution is appreciated!
The problem is that ctypes.Structure uses its own custom metaclass: _ctypes.StructType. Since you inherit the metaclass from Structure, Python does not know which metaclass to use when constructing your class.
You can fix this by inheriting your metaclass from _ctypes.StructType. Since the name of the metaclass is an implementation detail of the ctypes module, I recommend writing type(ctypes.Structure) to get the metaclass dynamically.
import ctypes
class VariableMeta(type(ctypes.Structure)):
pass
The drawback with this approach is that you limit the use of your metaclass. This might be OK if you only plan to use it for subclasses of ctypes.Structure.
Another approach is to create a intermediate metaclass that inherits from both metaclasses.
class PointsMetaClass(type(ctypes.Structure), VariableMeta):
pass
class Points(c.Structure, metaclass=PointsMetaClass):
# ...
Always make sure that you use super() instead of hard-coding type in your metaclass' __new__!
return super(VariableMeta, cls).__new__(cls, name, bases, dct)
As Guido once wrote: Writing metaclasses in Python will cause your head to explode!
This is an unusual question, but I'd like to dynamically generate the __slots__ attribute of the class based on whatever attributes I happened to have added to the class.
For example, if I have a class:
class A(object):
one = 1
two = 2
__slots__ = ['one', 'two']
I'd like to do this dynamically rather than specifying the arguments by hand, how would I do this?
At the point you're trying to define slots, the class hasn't been built yet, so you cannot define it dynamically from within the A class.
To get the behaviour you want, use a metaclass to introspect the definition of A and add a slots attribute.
class MakeSlots(type):
def __new__(cls, name, bases, attrs):
attrs['__slots__'] = attrs.keys()
return super(MakeSlots, cls).__new__(cls, name, bases, attrs)
class A(object):
one = 1
two = 2
__metaclass__ = MakeSlots
One very important thing to be aware of -- if those attributes stay in the class, the __slots__ generation will be useless... okay, maybe not useless -- it will make the class attributes read-only; probably not what you want.
The easy way is to say, "Okay, I'll initialize them to None, then let them disappear." Excellent! Here's one way to do that:
class B(object):
three = None
four = None
temp = vars() # get the local namespace as a dict()
__slots__ = temp.keys() # put their names into __slots__
__slots__.remove('temp') # remove non-__slots__ names
__slots__.remove('__module__') # now remove the names from the local
for name in __slots__: # namespace so we don't get read-only
del temp[name] # class attributes
del temp # and get rid of temp
If you want to keep those initial values it takes a bit more work... here's one possible solution:
class B(object):
three = 3
four = 4
def __init__(self):
for key, value in self.__init__.defaults.items():
setattr(self, key, value)
temp = vars()
__slots__ = temp.keys()
__slots__.remove('temp')
__slots__.remove('__module__')
__slots__.remove('__init__')
__init__.defaults = dict()
for name in __slots__:
__init__.defaults[name] = temp[name]
del temp[name]
del temp
As you can see, it is possible to do this without a metaclass -- but who wants all that boilerplate? A metaclass could definitely help us clean this up:
class MakeSlots(type):
def __new__(cls, name, bases, attrs):
new_attrs = {}
new_attrs['__slots__'] = slots = attrs.keys()
slots.remove('__module__')
slots.remove('__metaclass__')
new_attrs['__weakref__'] = None
new_attrs['__init__'] = init = new_init
init.defaults = dict()
for name in slots:
init.defaults[name] = attrs[name]
return super(MakeSlots, cls).__new__(cls, name, bases, new_attrs)
def new_init(self):
for key, value in self.__init__.defaults.items():
setattr(self, key, value)
class A(object):
__metaclass__ = MakeSlots
one = 1
two = 2
class B(object):
__metaclass__ = MakeSlots
three = 3
four = 4
Now all the tediousness is kept in the metaclass, and the actual class is easy to read and (hopefully!) understand.
If you need to have anything else in these classes besides attributes I strongly suggest you put whatever it is in a mixin class -- having them directly in the final class would complicate the metaclass even more.