简体   繁体   中英

Imported class populating properties from global variables

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 inspect ion 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM