class BaseClass(object):
def a_method(self, item_id):
response = lookup_response(item_id)
return response
class CachingClass(BaseClass):
def a_method(self, item_id):
if item_id in cache:
return item_from_cache
return super(CachingClass, self).a_method(item_id)
def uncached_method(self, item_id)
return super(CachingClass, self).a_method(item_id)
这样您就可以拆分如何查找响应和缓存的逻辑,同时还可以让 API 用户灵活地决定是否需要缓存功能。
Normally when writing method one could argue that a method / object should do one thing and it should do it well. If your method get more and more parameters which require more and more ifs in your code that probably means that your code is doing more then one thing. Especially if those parameters trigger totally different behavior. Instead maybe the same behavior could be produced by having different classes and having them overload methods.
Maybe you could use something like:
class BaseClass(object):
def a_method(self, item_id):
response = lookup_response(item_id)
return response
class CachingClass(BaseClass):
def a_method(self, item_id):
if item_id in cache:
return item_from_cache
return super(CachingClass, self).a_method(item_id)
def uncached_method(self, item_id)
return super(CachingClass, self).a_method(item_id)
That way you can split the logic of how to lookup the response and the caching while also making it flexible for the user of the API to decide if they want the caching capabilities or not.
B 类 中使用的方法没有任何问题。为了让您一目了然地知道您实际上需要包含 item_id 或 cached_api_response,我会首先进行错误检查:
class B:
def a_method(item_id = None, cached_api_response = None):
"""Requires either item_id or cached_api_response"""
if not ((item_id == None) ^ (cached_api_response == None)):
#error
# or, if you want to allow both,
if (item_id == None) and (cached_api_response == None):
# error
# you don't actually have to do this on one line
# also don't use it if cached_item_api_response can evaluate to 'False'
api_response = cached_item_api_response or # make api call using item_id
... #do stuff
There is nothing wrong with the method used in your class B. To make it more obvious at a glance that you actually need to include either item_id or cached_api_response, I would put the error checking first:
class B:
def a_method(item_id = None, cached_api_response = None):
"""Requires either item_id or cached_api_response"""
if not ((item_id == None) ^ (cached_api_response == None)):
#error
# or, if you want to allow both,
if (item_id == None) and (cached_api_response == None):
# error
# you don't actually have to do this on one line
# also don't use it if cached_item_api_response can evaluate to 'False'
api_response = cached_item_api_response or # make api call using item_id
... #do stuff
Ultimately this is a judgement that must be made for each situation. I would ask myself, which of these two more closely fits:
Two completely different algorithms or actions, with completely different semantics, even though they may be passed similar information
A single conceptual idea, with consistent semantics, but with nuance based on input
If the first is closest, go with separate methods. If the second is closest, go with optional arguments. You might even implement a single method by testing the type of the argument(s) to avoid passing additional arguments.
class API_Connection(object):
def do_something_with_api_response(self, response):
...
def do_something_else_with_api_response(self, response):
...
您在一个实例上有两个方法,并且您在它们之间显式传递状态?为什么这些方法而不是模块中的裸函数?
相反,请考虑使用封装来帮助您让类的实例拥有 api 响应。
例如:
class API_Connection(object):
def __init__(self, api_url):
self._url = api_url
self.cached_response = None
@property
def response(self):
"""Actually use the _url and get the response when needed."""
if self._cached_response is None:
# actually calculate self._cached_response by making our
# remote call, etc
self._cached_response = self._get_api_response(self._url)
return self._cached_response
def _get_api_response(self, api_param1, ...):
"""Make the request and return the api's response"""
def do_something_with_api_response(self):
# just use self.response
do_something(self.response)
def do_something_else_with_api_response(self):
# just use self.response
do_something_else(self.response)
您有缓存,任何需要此响应的方法都可以按任意顺序运行,而无需发出多个 api 请求,因为第一个需要 self.response 的方法将计算它,而其他每个方法都将使用缓存的值。希望很容易想象通过多个 URL 或 RPC 调用来扩展它。如果您需要很多缓存其返回值的方法,例如上面的响应,那么您应该为您的方法考虑一个记忆化装饰器。
This is an OO anti-pattern.
class API_Connection(object):
def do_something_with_api_response(self, response):
...
def do_something_else_with_api_response(self, response):
...
You have two methods on an instance and you're passing state between them explicitly? Why are these methods and not bare functions in a module?
Instead, think about using encapsulation to help you by having the instance of the class own the api response.
For example:
class API_Connection(object):
def __init__(self, api_url):
self._url = api_url
self.cached_response = None
@property
def response(self):
"""Actually use the _url and get the response when needed."""
if self._cached_response is None:
# actually calculate self._cached_response by making our
# remote call, etc
self._cached_response = self._get_api_response(self._url)
return self._cached_response
def _get_api_response(self, api_param1, ...):
"""Make the request and return the api's response"""
def do_something_with_api_response(self):
# just use self.response
do_something(self.response)
def do_something_else_with_api_response(self):
# just use self.response
do_something_else(self.response)
You have caching and any method which needs this response can run in any order without making multiple api requests because the first method that needs self.response will calculate it and every other will use the cached value. Hopefully it's easy to imagine extending this with multiple URLs or RPC calls. If you have a need for a lot of methods that cache their return values like response above then you should look into a memoization decorator for your methods.
发布评论
评论(5)
通常,在编写方法时,人们可能会认为方法/对象应该做一件事并且应该做得很好。如果您的方法获得越来越多的参数,而这些参数需要代码中越来越多的 if,则可能意味着您的代码正在做不止一件事。特别是如果这些参数触发完全不同的行为。相反,也许可以通过使用不同的类并让它们重载方法来产生相同的行为。
也许您可以使用类似的方法:
这样您就可以拆分如何查找响应和缓存的逻辑,同时还可以让 API 用户灵活地决定是否需要缓存功能。
Normally when writing method one could argue that a method / object should do one thing and it should do it well. If your method get more and more parameters which require more and more ifs in your code that probably means that your code is doing more then one thing. Especially if those parameters trigger totally different behavior. Instead maybe the same behavior could be produced by having different classes and having them overload methods.
Maybe you could use something like:
That way you can split the logic of how to lookup the response and the caching while also making it flexible for the user of the API to decide if they want the caching capabilities or not.
B 类
中使用的方法没有任何问题。为了让您一目了然地知道您实际上需要包含item_id
或cached_api_response
,我会首先进行错误检查:There is nothing wrong with the method used in your
class B
. To make it more obvious at a glance that you actually need to include eitheritem_id
orcached_api_response
, I would put the error checking first:最终,这是必须针对每种情况做出的判断。我会问自己,这两者中哪一个更适合:
如果第一个是最接近的,请使用单独的方法。如果第二个最接近,请使用可选参数。您甚至可以通过测试参数的类型来实现单个方法,以避免传递额外的参数。
Ultimately this is a judgement that must be made for each situation. I would ask myself, which of these two more closely fits:
If the first is closest, go with separate methods. If the second is closest, go with optional arguments. You might even implement a single method by testing the type of the argument(s) to avoid passing additional arguments.
这是面向对象的反模式。
您在一个实例上有两个方法,并且您在它们之间显式传递状态?为什么这些方法而不是模块中的裸函数?
相反,请考虑使用封装来帮助您让类的实例拥有 api 响应。
例如:
您有缓存,任何需要此响应的方法都可以按任意顺序运行,而无需发出多个 api 请求,因为第一个需要 self.response 的方法将计算它,而其他每个方法都将使用缓存的值。希望很容易想象通过多个 URL 或 RPC 调用来扩展它。如果您需要很多缓存其返回值的方法,例如上面的响应,那么您应该为您的方法考虑一个记忆化装饰器。
This is an OO anti-pattern.
You have two methods on an instance and you're passing state between them explicitly? Why are these methods and not bare functions in a module?
Instead, think about using encapsulation to help you by having the instance of the class own the api response.
For example:
You have caching and any method which needs this response can run in any order without making multiple api requests because the first method that needs
self.response
will calculate it and every other will use the cached value. Hopefully it's easy to imagine extending this with multiple URLs or RPC calls. If you have a need for a lot of methods that cache their return values likeresponse
above then you should look into a memoization decorator for your methods.缓存的响应应该保存在实例中,而不是像一袋彩虹糖一样传递——如果你把它掉了怎么办?
item_id
每个实例都是唯一的,还是一个实例可以查询多个实例?如果它可以有多个,我会采用这样的方法:如果您可能需要获取有关
item_id
的最新信息,您可以有一个refresh_response
方法。The cached response should be saved in the instance, not passed around like a bag of Skittles -- what if you dropped it?
Is
item_id
unique per instance, or can an instance make queries for more than one? If it can have more than one, I'd go with something like this:And if you may have to get the most current info about
item_id
, you can have arefresh_response
method.