如何解决创建具有大量变量的类的正确方法 输出
我在多年没有这样做之后最近开始编码,我正在构建一个小项目以重新开始。这是一个使用各种加密交易市场专用 API 的小型应用程序。
我的问题不是解决问题,而是解决问题的正确方法。因为我不喜欢有很多变量的编码模型,所以我决定创建一个抽象模型来为我完成定义变量的劳动密集型部分。
问题来了。创建一个需要很多变量的类时,我应该:
-
定义
__init__()
方法中的每个变量,并在__init__()
标头中包含每个变量或
-
我可以使用我的抽象类,它只需要定义所需的变量列表吗?我可以在未来的项目中做到这一点吗?
我的抽象类使用变量名的“静态”列表(我知道是 Python)并实现了构建动态/抽象对象的 __init__(self,*args,**kwargs)
方式。
我不知道应该为项目实现显式的笨重方法还是隐式的抽象方法。人们/团队在现实世界中使用的标准方法是什么?
示例
显式庞大方法
class Coin(object):
def __init__(self,id=None,base_currency=None,quote_currency=None,base_min_size=None,base_max_size=None,quote_increment=None,base_increment=None,display_name=None,min_market_funds=None,max_market_funds=None,margin_enabled=None,post_only=None,limit_only=None,cancel_only=None,trading_disabled=None,status=None,status_message=None):
self.id = id
self.base_currency = base_currency
self.quote_currency = quote_currency
self.base_min_size = base_min_size
self.base_max_size = base_max_size
self.quote_increment = quote_increment
self.base_increment = base_increment
self.display_name = display_name
self.min_market_funds = min_market_funds
self.max_market_funds = max_market_funds
self.margin_enabled = margin_enabled
self.post_only = post_only
self.limit_only = limit_only
self.cancel_only = cancel_only
self.trading_disabled = trading_disabled
self.status = status
self.status_message = status_message
隐式抽象方法
class EasyModel(object):
""" Abstract Model. Use to create models that might have extensive variable members,or in cases where applicable. """
__attrs__ = []
def __init__(self,**kwargs):
self.set_using_args(args) # set variables given as a *args
self.set_using_kwargs(kwargs) # set variables given as **kwargs
self.fill_empty() # Set variables that were expected to None
def set_using_args(self,args):
""" Given a list of variables (assuming in the correct order),add the value to the proper variable name. """
for i,arg_value in enumerate(args):
arg_name = self.__class__.__attrs__[i]
setattr(self,arg_name,arg_value)
def set_using_kwargs(self,kwargs):
""" Given a dictionary,add the parameters as variables to the instance if the parameter is valid. """
for arg_name,arg_value in kwargs.items():
if (arg_name in self.__class__.__attrs__):
setattr(self,arg_value)
def fill_empty(self):
""" Create any undefined variables and set as None """
for arg_name in self.__class__.__attrs__:
if (not hasattr(self,arg_name)):
setattr(self,None)
def __setattr__ (self,name,value):
""" Make sure that the attributes that will be added are expected. """
if (name in self.__class__.__attrs__):
self.__dict__[name] = value
class Coin(EasyModel):
""" Using the EasyModel to quickly build a model that will be created from an API request that gives all the data below in the form of a dictionary. """
__attrs__ = ['id','base_currency','quote_currency','base_min_size','base_max_size','quote_increment','base_increment','display_name','min_market_funds','max_market_funds','margin_enabled','post_only','limit_only','cancel_only','trading_disabled','status','status_message']
def __init__(self,**kwargs):
super(Coin,self).__init__(*args,**kwargs)
# Both would result in the same usage.
coin_data = {...} # Json / dictionary version of object
# Create object by passin json / dictionary as **kwargs
c = Coin(**coin_data)
# Both methods would result in this usage.
print (c.display_name)
解决方法
这最终比我想象的要难。我几乎没有触及元类和数据类的表面,但这是我想出的解决方案,包括最后的工作演示。
我不确定我会坚持哪种解决方案。
# Required for dataclass
from dataclasses import dataclass
# Required to get coin data used in example.
import cbpro
""" Resources Used:
https://www.geeksforgeeks.org/__new__-in-python/
https://www.pythonpool.com/python__new__/
I dont speak or read japanese but i found the snippets to be helpful.
https://python.ms/new/#_3-super-%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%82%92%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B%E3%80%82
"""
############################################################################################################
class CoinFunctions(object):
""" A class of functionalities for a coin. All the class varients will inheirt this class. """
def get_base_currency(self):
return self.base_currency
############################################################################################################
def create_coin(data):
""" Dynamically Create a type/class and apply the CoinFunctions's methods. """
coin_class = type("GeneratedCoin",(CoinFunctions,),data) # Build a new class. Still needs to be initialized
c = coin_class()
c.__dict__ = data
return c # Return an object initialized from the generated class.
############################################################################################################
class MetaCoin(CoinFunctions):
""" class that implements the __new__ method. Haven't got into inheriting from metaclasses yet. """
# Constructs the object to be initialized
def __new__(cls,**kwargs):
self = object.__new__(cls)
self.__dict__ = kwargs
return self # Returns the object
# Initializes the object.
def __init__(self,*args,**kwargs):
pass
############################################################################################################
@dataclass()
class Coin(CoinFunctions):
""" A dataclass that is used to build a proper data set of a coin with built-in types. Inherit from CoinFunctions for functionalities"""
id : str # base_currency-quote_currency (LTC-BTC)
base_currency : str # (LTC)
quote_currency : str # (BTC)
base_min_size : str # float
base_max_size : str # int / float
quote_increment : str # double
base_increment : str # double
display_name : str
min_market_funds : str # float
max_market_funds : str # int / float
margin_enabled : bool
post_only : bool
limit_only : bool
cancel_only : bool
trading_disabled : bool
status : str
status_message : str
############################################################################################################
"""
This is my original solution.
The __attrs__ field accepts a list of names that will be variables of the class.
The list of names that is __attrs__ are not only made into variables but are the only allowed variables.
If you want a variable in your class you must include its name inside of __attrs__.
When inheriting from EasyModel the only required ovveride is the __attrs__ field.
"""
class EasyModel(object):
""" Abstract Model. Use to create models that might have extensive variable members,or in cases where applicable. """
__attrs__ = []
def __init__(self,**kwargs):
self.set_using_args(args) # set variables given as a *args
self.set_using_kwargs(kwargs) # set variables given as **kwargs
self.fill_empty() # Set variables that were expected to None
def set_using_args(self,args):
""" Given a list of variables (assuming in the correct order),add the value to the proper variable name. """
#print (self.__class__.__attrs__)
for i,arg_value in enumerate(args):
#print ("\n\n********* WORKING IN ARGS *********\n\n")
arg_name = self.__class__.__attrs__[i]
setattr(self,arg_name,arg_value)
def set_using_kwargs(self,kwargs):
""" Given a dictionary,add the parameters as variables to the instance if the parameter is valid. """
for arg_name,arg_value in kwargs.items():
#print (arg_name in self.__class__.__attrs__)
if (arg_name in self.__class__.__attrs__):
setattr(self,arg_value)
def fill_empty(self):
""" Create any undefined variables and set as None """
for arg_name in self.__class__.__attrs__:
if (not hasattr(self,arg_name)):
setattr(self,None)
def __setattr__ (self,name,value):
""" Make sure that the attributes that will be added are expected. """
if (name in self.__class__.__attrs__):
self.__dict__[name] = value
class EasyCoin(EasyModel):
""" Using the EasyModel to quickly build a model that will be created from an API request that gives all the data below in the form of a dictionary. """
__attrs__ = ['id','base_currency','quote_currency','base_min_size','base_max_size','quote_increment','base_increment','display_name','min_market_funds','max_market_funds','margin_enabled','post_only','limit_only','cancel_only','trading_disabled','status','status_message']
def __init__(self,**kwargs):
super(EasyCoin,self).__init__(*args,**kwargs)
#########################################################################################################################################
#########################################################################################################################################
# Executing The Code:
if __name__ == '__main__':
# Make a connection to coinbase pro and request a list of their crypto trade markets.
client = cbpro.PublicClient()
#list_of_coins = client.get_currencies() # an actual list of coins
list_of_coins = client.get_products() # a list of supported crypto trades.
# Each item in the list_of_coins is a dictionary.
for coin_data in list_of_coins:
c = create_coin(coin_data)
print ("")
print ("Type:",type(c))
print ("Class Bases:",c.__class__.__bases__)
print ("Object:",c)
print ("Object.__dict__",c.__dict__)
c = MetaCoin(**coin_data)
print ("")
print ("Type:",c.__dict__)
c = Coin(**coin_data)
print ("")
print ("Type:",c.__dict__)
c = EasyCoin(**coin_data)
print ("")
print ("Type:",c.__dict__)
break
输出
Type: <class '__main__.GeneratedCoin'>
Class Bases: (<class '__main__.CoinFunctions'>,)
Object: <__main__.GeneratedCoin object at 0x043867D0>
Object.__dict__ {'id': 'LINK-USD','base_currency': 'LINK','quote_currency': 'USD','base_min_size': '0.1','base_max_size': '90000','quote_increment': '0.00001','base_increment': '0.01','display_name': 'LINK/USD','min_market_funds': '10','max_market_funds': '100000','margin_enabled': False,'post_only': False,'limit_only': False,'cancel_only': False,'trading_disabled': False,'status': 'online','status_message': ''}
Type: <class '__main__.MetaCoin'>
Class Bases: (<class '__main__.CoinFunctions'>,)
Object: <__main__.MetaCoin object at 0x04386730>
Object.__dict__ {'id': 'LINK-USD','status_message': ''}
Type: <class '__main__.Coin'>
Class Bases: (<class '__main__.CoinFunctions'>,)
Object: Coin(id='LINK-USD',base_currency='LINK',quote_currency='USD',base_min_size='0.1',base_max_size='90000',quote_increment='0.00001',base_increment='0.01',display_name='LINK/USD',min_market_funds='10',max_market_funds='100000',margin_enabled=False,post_only=False,limit_only=False,cancel_only=False,trading_disabled=False,status='online',status_message='')
Object.__dict__ {'id': 'LINK-USD','status_message': ''}
Type: <class '__main__.EasyCoin'>
Class Bases: (<class '__main__.EasyModel'>,)
Object: <__main__.EasyCoin object at 0x043867D0>
Object.__dict__ {'id': 'LINK-USD','status_message': ''}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。