1

I have devised a proxy class that allows me to replace any type/class with a factory method and still retain most of the functionality of the class object. Here is a sample of how it works:

class ProxyClass:
    def __init__(self, cls):
        self._ProxyClass_cls = cls
    def __getattr__(self, name):
        return getattr(self._ProxyClass_cls, name)

class _strProxy(ProxyClass):
    def __call__(self, s):
        if '\n' in s:
            raise ValueError
        if s not in self._cache:
            self._cache[s] = self._ProxyClass_cls(s)
        return self._cache[s]

str = _strProxy(str)
str._cache = {}


>>> s = str('hello')
>>> s
'hello'
>>> type(s)
<type 'str'>
>>> str('hello\n')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in __call__
ValueError

I like this implementation of factory methods because it completely replaces the original class object and still allows things like the following:

>>> map(str.split, [str('foo bar'), str('bar foo')])
[['foo', 'bar'], ['bar', 'foo']]

The only problem I have found is with operations on the class itself such as repr():

>>> repr(str)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor '__repr__' of 'str' object needs an argument

In this example, repr() is attempting to call str.__repr__() rather than type.__repr__(str). I tried fixing changing str.__class__ but found out that this is impossible in this case:

>>> str.__class__ = type
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __class__ must be set to a class

Does anyone know of any way to restore the functionality of repr(str) or maybe another way to accomplish what I am doing?

Matt
  • 21,026
  • 18
  • 63
  • 115
  • It is best in cases like this to post actual working code that we can copy/paste into a text file and execute. The above has `>>>` and `...` that must be edited out, and the above has a typo in it that makes it not work correctly even after the extra stuff is edited out. – steveha May 24 '12 at 21:38
  • @steveha Ok, I will edit it. What is the typo? – Matt May 24 '12 at 21:43
  • You have fixed the typo now. The class `_strProxy()` used to be `_strPproxy()`. (The calls were to `_strProxy()`.) – steveha May 25 '12 at 04:26

2 Answers2

0

Why not do

>>> class _strPproxy(ProxyClass):
...     def __call__(self, s):
...         self.s = s
...         if '\n' in s:
...             raise ValueError
...         if s not in self._cache:
...             self._cache[s] = self._ProxyClass_cls(s)
...         return self._cache[s]
...     def __repr__(self): 
...         return repr(self.s)
Jakob Bowyer
  • 33,878
  • 8
  • 76
  • 91
0

Make sure that all your classes inherit from object:

class ProxyClass(object):

In Python 3.x, all classes inherit from object anyway, so you don't need to do this. But in Python 2.x, the inheritance stuff doesn't work right unless all the classes involved are "new-style classes" that inherit from object.

repr(str) works fine for me after making the above change.

steveha
  • 74,789
  • 21
  • 92
  • 117
  • This has the same effect as Jakob's answer. You now cannot access the `__repr__` (or any of `object`'s methods) of the original `str`. – Matt May 24 '12 at 21:42
  • @Matt, the question shows binding the name `str` with a wrapped class. I wouldn't do that, myself; I would make a new class with a new name. But I am tired today, and I just answered the question instead of going full-on teacher mode. Even if you did rebind `str`, here is a way to access the `__repr__()` method of the original builtin `str`: `type('').__repr__(s)` – steveha May 25 '12 at 04:35
  • I dont intend to rebind built-ins like `str`, I just used it as an example. This is really for my own classes which I intend to define and rebind (probably with a decorator). This seems to be the only way I can come up with to override the constructor with a factory function and still make the class accessible as a class. – Matt May 25 '12 at 13:11
  • It's generally true in Python that you can explicitly ask for a method from a class directly. Like, suppose `s` is a variable which is an instance of a class derived from `str` and you want to print it using the `repr()` of `str`, you can do this: `str.__repr__(s)` More info here: http://stackoverflow.com/questions/7465070/are-python-object-functions-singletons/7465126#7465126 – steveha May 25 '12 at 20:11