How to list all staticmethods of a python class - python

I have a class somewhat like following:
from joblib import Memory
import time
def find_static_methods(cls):
# to be implemented
pass
class A:
def __init__(self, cache_path: str):
self._memory = Memory(cache_path, verbose=0)
self._methods = {}
for name, method in find_static_methods(A):
self._methods[name] = self._memory.cache(method)
def __getattribute__(self, item):
if item in self._methods:
return self._methods[item]
return super(A, self).__getattribute__(item)
#staticmethod
def method1(a: int, b: int):
time.sleep(3)
return a + b
I'm trying to memoize method1 using joblib.Memory. But I don't know the cache_path in advance. Please help me with the implementation of find_static_methods here.

Here is another way:
A_attrs = A.__dict__
for k, v in A_attrs.items():
if isinstance(v, staticmethod):
print(k)

Related

pydantic BaseModel with instance variable

I'm using pydantic with fastapi.
And, I make Model like this.
# model.py
from multiprocessing import RLock
from pydantic import BaseModel
class ModelA(BaseModel):
file_1: str = 'test'
def __init__(self, **data: Any):
super().__init__(**data)
self._lock = RLock()
self._instance_variable: int = 1
#property
def lock(self):
return self._lock
#property
def instance_variable(self) -> int:
with self.lock:
return self._instance_variable
#instance_variable.setter
def instance_variable(self, v: int) -> int:
assert isinstance(v, int)
with self.lock:
self._instance_variable = v
And I make test like this
# test_model_a.py
def test_model_a():
instance = ModelA()
assert instance.json() == '{"field_1": "test"}'
After, I run the test, but the instance can't create with this error.
E ValueError: "ModelA" object has no field "_lock"
So, How can I pass this test...?
Please... help me...
You need to use PrivateAttr field. And instead of a setter, use a workaround with __setattr__
from multiprocessing import RLock, synchronize
from typing import Any
from pydantic import BaseModel, PrivateAttr
class ModelA(BaseModel):
file_1: str = 'test'
_lock: synchronize.RLock = PrivateAttr()
_instance_variable: int = PrivateAttr()
def __init__(self, **data: Any):
super().__init__(**data)
self._lock = RLock()
self._instance_variable: int = 1
#property
def lock(self):
return self._lock
#property
def instance_variable(self) -> int:
with self.lock:
return self._instance_variable
def __setattr__(self, key, val):
if key == "instance_variable":
assert isinstance(val, int)
with self.lock:
self._instance_variable = val
return
super().__setattr__(key, val)
You can also avoid using a custom __init__
class ModelA(BaseModel):
file_1: str = 'test'
_lock: synchronize.RLock = PrivateAttr(default_factory=RLock)
_instance_variable: int = PrivateAttr(1)
# ...

Make method can be called from class or instance

I want to make a method to be called from class or instance.
For example :
class SomeClass:
a = 10
def __init__(self, val):
self.a = val
def print_a(self):
print(self.a)
SomeClass(20).print_a() # 20
SomeClass.print_a() # Error!
Here I want to make print_a can be called by class either.
If I use classmethod, the result is wrong.
class SomeClass:
a = 10
def __init__(self, val):
self.a = val
#classmethod
def print_a(cls):
print(cls.a)
SomeClass(20).print_a() # 10 (wrong!)
SomeClass.print_a() # 10
I hope the result is like this:
SomeClass(20).print_a() # 20
SomeClass.print_a() # 10
How can I achieve this?
classmethod is simply a descriptor object, you can read about how it could be implemented using pure python in the Descriptor HOWTO. Using that implementation as an inspiration:
from types import MethodType
class HybridMethod:
def __init__(self, f):
self.f = f
def __get__(self, obj, cls=None):
if obj is None:
return MethodType(self.f, cls)
else:
return MethodType(self.f, obj)
class SomeClass:
a = 10
def __init__(self, val):
self.a = val
#HybridMethod
def print_a(self):
print(self.a)
SomeClass(20).print_a()
SomeClass.print_a()

Using __setattr__ & __getattr__ with properties

I'm attempting to write a more pythonic interaction with win32com.client for my own use so I can do things like:
with Presentation(close=True) as P:
table = P[0].tables[0]
table.cells.Shape.TextFrame.TextRange.Text= 'hello'
I've managed to get the above working (very satisfying) by overloading __getattr__ and __setattr__.
I want to interact with a powerpoint table as an array not a linear object so I created the CellRange object
This is from tables.py, which handles the array views of win32com.client tables.
from itertools import product
import numpy as np
from win32com.client import pywintypes
ALIGN_LABELS = 'bottom bottom_base middle top top_base mixed'.split()
ALIGN_LABELS_N = {k: i for i, k in enumerate(ALIGN_LABELS)}
class Win32Interface(object):
def __init__(self, win32_object):
super(Win32Interface, self).__setattr__('win32_object', win32_object)
def __setattr__(self, k, v):
setattr(self.win32_object, k, v)
def __getattr__(self, v):
return getattr(self.win32_object, v)
class Cell(Win32Interface):
def __repr__(self):
return self.Shape.TextFrame.TextRange.Text
#property
def text(self):
return self.Shape.TextFrame.TextRange.Text
#text.setter
def text(self, v):
setattr(self.Shape.TextFrame.TextRange, 'Text', v)
class CellRange(object):
def __init__(self, cell_array):
super(CellRange, self).__init__()
super(CellRange, self).__setattr__('cell_array', cell_array)
def _apply_map(self, f):
func = np.vectorize(f)
return func(self.cell_array)
def __getattr__(self, k):
try:
arr = self._apply_map(lambda x: getattr(x, k))
return CellRange(arr)
except (AttributeError, pywintypes.com_error):
return getattr(self.cell_array, k)
def __setattr__(self, k, v):
if hasattr(v, 'shape'):
assert self.shape == v.shape, 'mismatched shape'
for cell, value in zip(self.cell_array.ravel(), v.ravel()):
cell.__setattr__(k, value)
else:
self._apply_map(lambda x: setattr(x, k, v))
def __repr__(self):
return self.cell_array.__repr__()
Ignoring the Table object for the moment, I want to know why
cell_range = CellRange(cell_array)
cell_range.text = 'hello'
throws up a cannot be set error. The above calls __setattr__ which then calls _apply_map to set each element of the array, this calls Cell.__setattr__. Why can I do print cell_range.text but not cell_range.text = 'hello'?
Stumbled into the solution about 10 minutes after I posted!
The answer is to use Object's __setattr__ instead of Win32Interface's
So obvious!
class Win32Interface(object):
def __init__(self, win32_object):
super(Win32Interface, self).__setattr__('win32_object', win32_object)
def __setattr__(self, k, v):
if k in self.properties:
super(Win32Interface, self).__setattr__(k, v)
else:
setattr(self.win32_object, k, v)
def __getattr__(self, v):
return getattr(self.win32_object, v)
#property
def properties(self):
class_items = self.__class__.__dict__.iteritems()
return {k:v for k, v in class_items if isinstance(v, property) and k != 'properties'}

Classmethod Item Assignment

So I have essentially created a dictionary Class that uses classmethods for all of its magic methods that looks like this:
class ClassDict(object):
_items = {}
#classmethod
def __getitem__(cls, key):
return cls._items[key]
#classmethod
def __setitem__(cls, key, val):
cls._items[key] = val
#classmethod
def __len__(cls):
return len(cls._items)
#classmethod
def __delitem__(cls, key):
cls._items.__delitem__(key)
#classmethod
def __iter__(cls):
return iter(cls._items)
And so when I try to assign an item to it:
ClassDict['item'] = 'test'
I get an error saying TypeError: 'type' object does not support item assignment, but if I call the actual method, __setitem__ like so it works fine:
ClassDict.__setitem__('item', 'test')
And this also works:
ClassDict().__setitem__('item', 'test')
Is there anything I am doing wrong here that would prevent the first example from working? Is there any way I can fix this issue?
To get the behavior desired of being able to do:
ClassDict['item'] = 'test'
I had to implement the special methods as a metaclass instead as Martijn pointed out.
So my final implementation looks like this:
class MetaClassDict(type):
_items = {}
#classmethod
def __getitem__(cls, key):
return cls._items[key]
#classmethod
def __setitem__(cls, key, val):
cls._items[key] = val
#classmethod
def __len__(cls):
return len(cls._items)
#classmethod
def __delitem__(cls, key):
cls._items.__delitem__(key)
#classmethod
def __iter__(cls):
return iter(cls._items)
class ClassDict(object):
__metaclass__ = MetaClassDict

Is there a better way to create a class which is a subclass of `dict` but case-insensitive?

I want to create a class inheriting from dict type but could use case-insensitive key to visit the data. I implement a simple one but I don't think using instance.__dict__ variable is a proper approach.
Is there a better way to do this?
Here is my code:
class MyDict(dict):
def __init__(self, *args, **kwargs):
if args:
for k, v in args[0].iteritems():
self.__dict__.update({k.lower(): v})
def __getitem__(self, k):
return self.__dict__.get(k.lower())
def __setitem__(self, k, v):
self.__dict__.update({k.lower(): v})
def __delitem__(self, k):
self.__dict__.pop(k, None)
if __name__ == '__main__':
test_0 = MyDict({'naME': 'python', 'Age': 24})
print(test_0['name']) # return 'python'
print(test_0['AGE']) # return 24
test_1 = MyDict()
test_1['StaCk'] = 23
print(test_1['stack']) # return 23
print(test_1['STACK']) # return 23
Edit: See Janne Karila's link, that contains a better solution.
Instead of using self.__dict__, which has a special meaning unrelated to this being a dict, you should use super() to call the corresponding function on the superclass.
E.g.,
def __setitem__(self, k, v):
if hasattr(k, 'lower'):
k = k.lower()
return super(MyDict, self).__setitem__(k, v)

Categories

Resources