Wrapping homogeneous Python objectsPython Object WrapperCalling an external command in PythonWhat are metaclasses in Python?Is there a way to run Python on Android?Finding the index of an item given a list containing it in PythonDifference between append vs. extend list methods in PythonHow can I safely create a nested directory in Python?Does Python have a ternary conditional operator?How to get the current time in PythonHow can I make a time delay in Python?Does Python have a string 'contains' substring method?
Did the UK lift the requirement for registering SIM cards?
Why should universal income be universal?
Is it ethical to recieve stipend after publishing enough papers?
What are some good ways to treat frozen vegetables such that they behave like fresh vegetables when stir frying them?
I found an audio circuit and I built it just fine, but I find it a bit too quiet. How do I amplify the output so that it is a bit louder?
Stack Interview Code methods made from class Node and Smart Pointers
Which Article Helped Get Rid of Technobabble in RPGs?
JIS and ISO square taper
Temporarily disable WLAN internet access for children, but allow it for adults
Does the reader need to like the PoV character?
Mimic lecturing on blackboard, facing audience
Does grappling negate Mirror Image?
Giving feedback to someone without sounding prejudiced
Will the Sticky MAC access policy prevent unauthorized hubs from connecting to a network?
How do I fix the group tension caused by my character stealing and possibly killing without provocation?
The IT department bottlenecks progress, how should I handle this?
sp_blitzCache against one stored procedure
Doesn't the system of the Supreme Court oppose justice?
Confused about Cramer-Rao lower bound and CLT
Why do ¬, ∀ and ∃ have the same precedence?
Microchip documentation does not label CAN buss pins on micro controller pinout diagram
What to do when eye contact makes your coworker uncomfortable?
What is the English pronunciation of "pain au chocolat"?
How to convince somebody that he is fit for something else, but not this job?
Wrapping homogeneous Python objects
Python Object WrapperCalling an external command in PythonWhat are metaclasses in Python?Is there a way to run Python on Android?Finding the index of an item given a list containing it in PythonDifference between append vs. extend list methods in PythonHow can I safely create a nested directory in Python?Does Python have a ternary conditional operator?How to get the current time in PythonHow can I make a time delay in Python?Does Python have a string 'contains' substring method?
I'm looking for a way to have a collection of homogeneous objects, wrap them in another object, but have the wrapper object have the same API as the original and forward the corresponding API call to its object members.
class OriginalApi:
def __init__(self):
self.a = 1
self.b = "bee"
def do_something(self, new_a, new_b, put_them_together=None):
self.a = new_a or self.a
self.b = new_b or self.b
if put_them_together is not None:
self.b = "".format(self.a, self.b)
# etc.
class WrappedApi:
def __init__(self):
self.example_1 = OriginalApi()
self.example_2 = OriginalApi()
Some possible solutions that have been considered, but are inadequate:
Rewriting the whole API Why not? Not adequate because the API is fairly large and expanding. Having to maintain the API in multiple spots is not realistic.
Code example:
class WrappedApi:
def __init__(self):
self.example_1 = OriginalApi()
self.example_2 = OriginalApi()
def do_something(self, new_a, new_b, put_them_together=None):
self.example_1.do_something(new_a, new_b, put_them_together)
self.example_2.do_something(new_a, new_b, put_them_together)Using a list and a for-loop This changes the API on the object. That said, this is the backup solution in the event I can't find something more elegant. In this case, the
WrappedApiclass would not exist.Code example:
wrapped_apis = [OriginalApi(), OriginalApi()]
for wrapped_api in wrapped_apis:
wrapped_api.do_something(1, 2, True)I tried using
Python Object Wrapper, but I could not see how to have it call multiple sub-objects with the same arguments.
And for anyone curious about the use case, it's actually a collection of several matplotlib axes objects. I don't want to reimplement to entire axes API (it's big), and I don't want to change all the code that makes calls on axes (like plot, step, etc.)
python
add a comment |
I'm looking for a way to have a collection of homogeneous objects, wrap them in another object, but have the wrapper object have the same API as the original and forward the corresponding API call to its object members.
class OriginalApi:
def __init__(self):
self.a = 1
self.b = "bee"
def do_something(self, new_a, new_b, put_them_together=None):
self.a = new_a or self.a
self.b = new_b or self.b
if put_them_together is not None:
self.b = "".format(self.a, self.b)
# etc.
class WrappedApi:
def __init__(self):
self.example_1 = OriginalApi()
self.example_2 = OriginalApi()
Some possible solutions that have been considered, but are inadequate:
Rewriting the whole API Why not? Not adequate because the API is fairly large and expanding. Having to maintain the API in multiple spots is not realistic.
Code example:
class WrappedApi:
def __init__(self):
self.example_1 = OriginalApi()
self.example_2 = OriginalApi()
def do_something(self, new_a, new_b, put_them_together=None):
self.example_1.do_something(new_a, new_b, put_them_together)
self.example_2.do_something(new_a, new_b, put_them_together)Using a list and a for-loop This changes the API on the object. That said, this is the backup solution in the event I can't find something more elegant. In this case, the
WrappedApiclass would not exist.Code example:
wrapped_apis = [OriginalApi(), OriginalApi()]
for wrapped_api in wrapped_apis:
wrapped_api.do_something(1, 2, True)I tried using
Python Object Wrapper, but I could not see how to have it call multiple sub-objects with the same arguments.
And for anyone curious about the use case, it's actually a collection of several matplotlib axes objects. I don't want to reimplement to entire axes API (it's big), and I don't want to change all the code that makes calls on axes (like plot, step, etc.)
python
2
How do you want to handle the sequence protocol? Should indexing into the wrapper give you one of the wrapped items, or the result of indexing into all of the wrapped items? This is going to be complex in the generic case.
– jonrsharpe
Mar 17 at 19:58
is this referring todef __getitem__(self, i):? If so, I hadn't thought about that, but I think either method would be valid for my situation
– TinyTheBrontosaurus
Mar 17 at 20:02
add a comment |
I'm looking for a way to have a collection of homogeneous objects, wrap them in another object, but have the wrapper object have the same API as the original and forward the corresponding API call to its object members.
class OriginalApi:
def __init__(self):
self.a = 1
self.b = "bee"
def do_something(self, new_a, new_b, put_them_together=None):
self.a = new_a or self.a
self.b = new_b or self.b
if put_them_together is not None:
self.b = "".format(self.a, self.b)
# etc.
class WrappedApi:
def __init__(self):
self.example_1 = OriginalApi()
self.example_2 = OriginalApi()
Some possible solutions that have been considered, but are inadequate:
Rewriting the whole API Why not? Not adequate because the API is fairly large and expanding. Having to maintain the API in multiple spots is not realistic.
Code example:
class WrappedApi:
def __init__(self):
self.example_1 = OriginalApi()
self.example_2 = OriginalApi()
def do_something(self, new_a, new_b, put_them_together=None):
self.example_1.do_something(new_a, new_b, put_them_together)
self.example_2.do_something(new_a, new_b, put_them_together)Using a list and a for-loop This changes the API on the object. That said, this is the backup solution in the event I can't find something more elegant. In this case, the
WrappedApiclass would not exist.Code example:
wrapped_apis = [OriginalApi(), OriginalApi()]
for wrapped_api in wrapped_apis:
wrapped_api.do_something(1, 2, True)I tried using
Python Object Wrapper, but I could not see how to have it call multiple sub-objects with the same arguments.
And for anyone curious about the use case, it's actually a collection of several matplotlib axes objects. I don't want to reimplement to entire axes API (it's big), and I don't want to change all the code that makes calls on axes (like plot, step, etc.)
python
I'm looking for a way to have a collection of homogeneous objects, wrap them in another object, but have the wrapper object have the same API as the original and forward the corresponding API call to its object members.
class OriginalApi:
def __init__(self):
self.a = 1
self.b = "bee"
def do_something(self, new_a, new_b, put_them_together=None):
self.a = new_a or self.a
self.b = new_b or self.b
if put_them_together is not None:
self.b = "".format(self.a, self.b)
# etc.
class WrappedApi:
def __init__(self):
self.example_1 = OriginalApi()
self.example_2 = OriginalApi()
Some possible solutions that have been considered, but are inadequate:
Rewriting the whole API Why not? Not adequate because the API is fairly large and expanding. Having to maintain the API in multiple spots is not realistic.
Code example:
class WrappedApi:
def __init__(self):
self.example_1 = OriginalApi()
self.example_2 = OriginalApi()
def do_something(self, new_a, new_b, put_them_together=None):
self.example_1.do_something(new_a, new_b, put_them_together)
self.example_2.do_something(new_a, new_b, put_them_together)Using a list and a for-loop This changes the API on the object. That said, this is the backup solution in the event I can't find something more elegant. In this case, the
WrappedApiclass would not exist.Code example:
wrapped_apis = [OriginalApi(), OriginalApi()]
for wrapped_api in wrapped_apis:
wrapped_api.do_something(1, 2, True)I tried using
Python Object Wrapper, but I could not see how to have it call multiple sub-objects with the same arguments.
And for anyone curious about the use case, it's actually a collection of several matplotlib axes objects. I don't want to reimplement to entire axes API (it's big), and I don't want to change all the code that makes calls on axes (like plot, step, etc.)
python
python
edited Mar 17 at 23:33
Peter Mortensen
13.8k1987113
13.8k1987113
asked Mar 17 at 19:46
TinyTheBrontosaurusTinyTheBrontosaurus
1,34011125
1,34011125
2
How do you want to handle the sequence protocol? Should indexing into the wrapper give you one of the wrapped items, or the result of indexing into all of the wrapped items? This is going to be complex in the generic case.
– jonrsharpe
Mar 17 at 19:58
is this referring todef __getitem__(self, i):? If so, I hadn't thought about that, but I think either method would be valid for my situation
– TinyTheBrontosaurus
Mar 17 at 20:02
add a comment |
2
How do you want to handle the sequence protocol? Should indexing into the wrapper give you one of the wrapped items, or the result of indexing into all of the wrapped items? This is going to be complex in the generic case.
– jonrsharpe
Mar 17 at 19:58
is this referring todef __getitem__(self, i):? If so, I hadn't thought about that, but I think either method would be valid for my situation
– TinyTheBrontosaurus
Mar 17 at 20:02
2
2
How do you want to handle the sequence protocol? Should indexing into the wrapper give you one of the wrapped items, or the result of indexing into all of the wrapped items? This is going to be complex in the generic case.
– jonrsharpe
Mar 17 at 19:58
How do you want to handle the sequence protocol? Should indexing into the wrapper give you one of the wrapped items, or the result of indexing into all of the wrapped items? This is going to be complex in the generic case.
– jonrsharpe
Mar 17 at 19:58
is this referring to
def __getitem__(self, i):? If so, I hadn't thought about that, but I think either method would be valid for my situation– TinyTheBrontosaurus
Mar 17 at 20:02
is this referring to
def __getitem__(self, i):? If so, I hadn't thought about that, but I think either method would be valid for my situation– TinyTheBrontosaurus
Mar 17 at 20:02
add a comment |
2 Answers
2
active
oldest
votes
If you're only implementing methods then a generic __getattr__ can do the trick
class Wrapper:
def __init__(self, x):
self.x = x
def __getattr__(self, name):
def f(*args, **kwargs):
for y in self.x:
getattr(y, name)(*args, **kwargs)
return f
For example with x = Wrapper([[], [], []]) after calling x.append(12) all the three list objects will have 12 as last element.
Note that the return value will always be None... an option could be collecting return values and returning them as a list but this of course would "break the API".
This only works for methods and other callable attributes, though. Gathering the results into a list would be straightforward, but it'd be hard to distinguish between the cases where you have a list ofNones and should return the list, or a list ofNones and should returnNone.
– jonrsharpe
Mar 17 at 20:07
@jonrsharpe: yes of course, but it's hard to multiplex a protocol that requires reading without changing the API. What shouldlen(x)return if the contained objects don't answer the same?
– 6502
Mar 17 at 20:10
Well, quite! As I said above, in the generic case this gets very complex.
– jonrsharpe
Mar 17 at 20:10
good point on the return values. i'm hoping my usage ofmatplotlibgets away with ignoring most return values. And if it doesn't? Then... well... this get a lot harder. I'll ask a new question if that's the case.
– TinyTheBrontosaurus
Mar 17 at 22:36
add a comment |
I think you have the right idea here
wrapped_apis = [OriginalApi(), OriginalApi()]
for wrapped_api in wrapped_apis:
wrapped_api.do_something(1, 2, True)
You can define your wrapper class by inheriting from list and then handle the API calls to its items once it is created.
class WrapperClass(list):
def __init__(self, api_type):
self.api_type = api_type
for func in dir(api_type):
if callable(getattr(api_type, func)) and not func.startswith("__"):
setattr(self, func, lambda *args, **kwargs:
[getattr(o, func)(*args, **kwargs) for o in self])
w = WrapperClass(OriginalApi)
o1, o2 = [OriginalApi()]*2
w.append(o1)
w.append(o2)
print(w.do_something(1, 2, True))
# [None, None]
print(w[0].b)
# 12
print(w[1].b)
# 12
print(o1.b)
# 12
Here, I'm iterating every method in your API class and creating a method in the wrapper class that applies its arguments to all its list items. It then returns a list comprehension consisting of the results.
Needless to say, you should probably validate the type of a new object being appended to this WrapperClass like so,
def append(self, item):
if not isinstance(item, self.api_type):
raise TypeError('Wrong API type. Expected %s'.format(self.api_type))
super(WrapperClass, self).append(item)
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55211193%2fwrapping-homogeneous-python-objects%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
If you're only implementing methods then a generic __getattr__ can do the trick
class Wrapper:
def __init__(self, x):
self.x = x
def __getattr__(self, name):
def f(*args, **kwargs):
for y in self.x:
getattr(y, name)(*args, **kwargs)
return f
For example with x = Wrapper([[], [], []]) after calling x.append(12) all the three list objects will have 12 as last element.
Note that the return value will always be None... an option could be collecting return values and returning them as a list but this of course would "break the API".
This only works for methods and other callable attributes, though. Gathering the results into a list would be straightforward, but it'd be hard to distinguish between the cases where you have a list ofNones and should return the list, or a list ofNones and should returnNone.
– jonrsharpe
Mar 17 at 20:07
@jonrsharpe: yes of course, but it's hard to multiplex a protocol that requires reading without changing the API. What shouldlen(x)return if the contained objects don't answer the same?
– 6502
Mar 17 at 20:10
Well, quite! As I said above, in the generic case this gets very complex.
– jonrsharpe
Mar 17 at 20:10
good point on the return values. i'm hoping my usage ofmatplotlibgets away with ignoring most return values. And if it doesn't? Then... well... this get a lot harder. I'll ask a new question if that's the case.
– TinyTheBrontosaurus
Mar 17 at 22:36
add a comment |
If you're only implementing methods then a generic __getattr__ can do the trick
class Wrapper:
def __init__(self, x):
self.x = x
def __getattr__(self, name):
def f(*args, **kwargs):
for y in self.x:
getattr(y, name)(*args, **kwargs)
return f
For example with x = Wrapper([[], [], []]) after calling x.append(12) all the three list objects will have 12 as last element.
Note that the return value will always be None... an option could be collecting return values and returning them as a list but this of course would "break the API".
This only works for methods and other callable attributes, though. Gathering the results into a list would be straightforward, but it'd be hard to distinguish between the cases where you have a list ofNones and should return the list, or a list ofNones and should returnNone.
– jonrsharpe
Mar 17 at 20:07
@jonrsharpe: yes of course, but it's hard to multiplex a protocol that requires reading without changing the API. What shouldlen(x)return if the contained objects don't answer the same?
– 6502
Mar 17 at 20:10
Well, quite! As I said above, in the generic case this gets very complex.
– jonrsharpe
Mar 17 at 20:10
good point on the return values. i'm hoping my usage ofmatplotlibgets away with ignoring most return values. And if it doesn't? Then... well... this get a lot harder. I'll ask a new question if that's the case.
– TinyTheBrontosaurus
Mar 17 at 22:36
add a comment |
If you're only implementing methods then a generic __getattr__ can do the trick
class Wrapper:
def __init__(self, x):
self.x = x
def __getattr__(self, name):
def f(*args, **kwargs):
for y in self.x:
getattr(y, name)(*args, **kwargs)
return f
For example with x = Wrapper([[], [], []]) after calling x.append(12) all the three list objects will have 12 as last element.
Note that the return value will always be None... an option could be collecting return values and returning them as a list but this of course would "break the API".
If you're only implementing methods then a generic __getattr__ can do the trick
class Wrapper:
def __init__(self, x):
self.x = x
def __getattr__(self, name):
def f(*args, **kwargs):
for y in self.x:
getattr(y, name)(*args, **kwargs)
return f
For example with x = Wrapper([[], [], []]) after calling x.append(12) all the three list objects will have 12 as last element.
Note that the return value will always be None... an option could be collecting return values and returning them as a list but this of course would "break the API".
edited Mar 17 at 20:10
answered Mar 17 at 20:05
65026502
87.4k13115217
87.4k13115217
This only works for methods and other callable attributes, though. Gathering the results into a list would be straightforward, but it'd be hard to distinguish between the cases where you have a list ofNones and should return the list, or a list ofNones and should returnNone.
– jonrsharpe
Mar 17 at 20:07
@jonrsharpe: yes of course, but it's hard to multiplex a protocol that requires reading without changing the API. What shouldlen(x)return if the contained objects don't answer the same?
– 6502
Mar 17 at 20:10
Well, quite! As I said above, in the generic case this gets very complex.
– jonrsharpe
Mar 17 at 20:10
good point on the return values. i'm hoping my usage ofmatplotlibgets away with ignoring most return values. And if it doesn't? Then... well... this get a lot harder. I'll ask a new question if that's the case.
– TinyTheBrontosaurus
Mar 17 at 22:36
add a comment |
This only works for methods and other callable attributes, though. Gathering the results into a list would be straightforward, but it'd be hard to distinguish between the cases where you have a list ofNones and should return the list, or a list ofNones and should returnNone.
– jonrsharpe
Mar 17 at 20:07
@jonrsharpe: yes of course, but it's hard to multiplex a protocol that requires reading without changing the API. What shouldlen(x)return if the contained objects don't answer the same?
– 6502
Mar 17 at 20:10
Well, quite! As I said above, in the generic case this gets very complex.
– jonrsharpe
Mar 17 at 20:10
good point on the return values. i'm hoping my usage ofmatplotlibgets away with ignoring most return values. And if it doesn't? Then... well... this get a lot harder. I'll ask a new question if that's the case.
– TinyTheBrontosaurus
Mar 17 at 22:36
This only works for methods and other callable attributes, though. Gathering the results into a list would be straightforward, but it'd be hard to distinguish between the cases where you have a list of
Nones and should return the list, or a list of Nones and should return None.– jonrsharpe
Mar 17 at 20:07
This only works for methods and other callable attributes, though. Gathering the results into a list would be straightforward, but it'd be hard to distinguish between the cases where you have a list of
Nones and should return the list, or a list of Nones and should return None.– jonrsharpe
Mar 17 at 20:07
@jonrsharpe: yes of course, but it's hard to multiplex a protocol that requires reading without changing the API. What should
len(x) return if the contained objects don't answer the same?– 6502
Mar 17 at 20:10
@jonrsharpe: yes of course, but it's hard to multiplex a protocol that requires reading without changing the API. What should
len(x) return if the contained objects don't answer the same?– 6502
Mar 17 at 20:10
Well, quite! As I said above, in the generic case this gets very complex.
– jonrsharpe
Mar 17 at 20:10
Well, quite! As I said above, in the generic case this gets very complex.
– jonrsharpe
Mar 17 at 20:10
good point on the return values. i'm hoping my usage of
matplotlib gets away with ignoring most return values. And if it doesn't? Then... well... this get a lot harder. I'll ask a new question if that's the case.– TinyTheBrontosaurus
Mar 17 at 22:36
good point on the return values. i'm hoping my usage of
matplotlib gets away with ignoring most return values. And if it doesn't? Then... well... this get a lot harder. I'll ask a new question if that's the case.– TinyTheBrontosaurus
Mar 17 at 22:36
add a comment |
I think you have the right idea here
wrapped_apis = [OriginalApi(), OriginalApi()]
for wrapped_api in wrapped_apis:
wrapped_api.do_something(1, 2, True)
You can define your wrapper class by inheriting from list and then handle the API calls to its items once it is created.
class WrapperClass(list):
def __init__(self, api_type):
self.api_type = api_type
for func in dir(api_type):
if callable(getattr(api_type, func)) and not func.startswith("__"):
setattr(self, func, lambda *args, **kwargs:
[getattr(o, func)(*args, **kwargs) for o in self])
w = WrapperClass(OriginalApi)
o1, o2 = [OriginalApi()]*2
w.append(o1)
w.append(o2)
print(w.do_something(1, 2, True))
# [None, None]
print(w[0].b)
# 12
print(w[1].b)
# 12
print(o1.b)
# 12
Here, I'm iterating every method in your API class and creating a method in the wrapper class that applies its arguments to all its list items. It then returns a list comprehension consisting of the results.
Needless to say, you should probably validate the type of a new object being appended to this WrapperClass like so,
def append(self, item):
if not isinstance(item, self.api_type):
raise TypeError('Wrong API type. Expected %s'.format(self.api_type))
super(WrapperClass, self).append(item)
add a comment |
I think you have the right idea here
wrapped_apis = [OriginalApi(), OriginalApi()]
for wrapped_api in wrapped_apis:
wrapped_api.do_something(1, 2, True)
You can define your wrapper class by inheriting from list and then handle the API calls to its items once it is created.
class WrapperClass(list):
def __init__(self, api_type):
self.api_type = api_type
for func in dir(api_type):
if callable(getattr(api_type, func)) and not func.startswith("__"):
setattr(self, func, lambda *args, **kwargs:
[getattr(o, func)(*args, **kwargs) for o in self])
w = WrapperClass(OriginalApi)
o1, o2 = [OriginalApi()]*2
w.append(o1)
w.append(o2)
print(w.do_something(1, 2, True))
# [None, None]
print(w[0].b)
# 12
print(w[1].b)
# 12
print(o1.b)
# 12
Here, I'm iterating every method in your API class and creating a method in the wrapper class that applies its arguments to all its list items. It then returns a list comprehension consisting of the results.
Needless to say, you should probably validate the type of a new object being appended to this WrapperClass like so,
def append(self, item):
if not isinstance(item, self.api_type):
raise TypeError('Wrong API type. Expected %s'.format(self.api_type))
super(WrapperClass, self).append(item)
add a comment |
I think you have the right idea here
wrapped_apis = [OriginalApi(), OriginalApi()]
for wrapped_api in wrapped_apis:
wrapped_api.do_something(1, 2, True)
You can define your wrapper class by inheriting from list and then handle the API calls to its items once it is created.
class WrapperClass(list):
def __init__(self, api_type):
self.api_type = api_type
for func in dir(api_type):
if callable(getattr(api_type, func)) and not func.startswith("__"):
setattr(self, func, lambda *args, **kwargs:
[getattr(o, func)(*args, **kwargs) for o in self])
w = WrapperClass(OriginalApi)
o1, o2 = [OriginalApi()]*2
w.append(o1)
w.append(o2)
print(w.do_something(1, 2, True))
# [None, None]
print(w[0].b)
# 12
print(w[1].b)
# 12
print(o1.b)
# 12
Here, I'm iterating every method in your API class and creating a method in the wrapper class that applies its arguments to all its list items. It then returns a list comprehension consisting of the results.
Needless to say, you should probably validate the type of a new object being appended to this WrapperClass like so,
def append(self, item):
if not isinstance(item, self.api_type):
raise TypeError('Wrong API type. Expected %s'.format(self.api_type))
super(WrapperClass, self).append(item)
I think you have the right idea here
wrapped_apis = [OriginalApi(), OriginalApi()]
for wrapped_api in wrapped_apis:
wrapped_api.do_something(1, 2, True)
You can define your wrapper class by inheriting from list and then handle the API calls to its items once it is created.
class WrapperClass(list):
def __init__(self, api_type):
self.api_type = api_type
for func in dir(api_type):
if callable(getattr(api_type, func)) and not func.startswith("__"):
setattr(self, func, lambda *args, **kwargs:
[getattr(o, func)(*args, **kwargs) for o in self])
w = WrapperClass(OriginalApi)
o1, o2 = [OriginalApi()]*2
w.append(o1)
w.append(o2)
print(w.do_something(1, 2, True))
# [None, None]
print(w[0].b)
# 12
print(w[1].b)
# 12
print(o1.b)
# 12
Here, I'm iterating every method in your API class and creating a method in the wrapper class that applies its arguments to all its list items. It then returns a list comprehension consisting of the results.
Needless to say, you should probably validate the type of a new object being appended to this WrapperClass like so,
def append(self, item):
if not isinstance(item, self.api_type):
raise TypeError('Wrong API type. Expected %s'.format(self.api_type))
super(WrapperClass, self).append(item)
edited Mar 17 at 21:14
answered Mar 17 at 21:02
darkskydarksky
1,4261224
1,4261224
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55211193%2fwrapping-homogeneous-python-objects%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
How do you want to handle the sequence protocol? Should indexing into the wrapper give you one of the wrapped items, or the result of indexing into all of the wrapped items? This is going to be complex in the generic case.
– jonrsharpe
Mar 17 at 19:58
is this referring to
def __getitem__(self, i):? If so, I hadn't thought about that, but I think either method would be valid for my situation– TinyTheBrontosaurus
Mar 17 at 20:02