From a302bc93c5c887d51dcadc4b649590eebe645937 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Wed, 30 Jan 2019 13:07:23 +0800 Subject: [PATCH] [Mod] format code with autopep8 --- .flake8 | 3 + .style.yapf | 15 ----- README.md | 7 ++- binding/generator/autocxxpy/cxxparser.py | 44 +++++++------- binding/generator/autocxxpy/preprocessor.py | 7 ++- vnpy/api/rest/rest_client.py | 2 +- vnpy/api/websocket/websocket_client.py | 6 +- vnpy/app/cta_strategy/backtesting.py | 67 +++++++++++---------- vnpy/app/cta_strategy/ui/widget.py | 2 - vnpy/event/engine.py | 2 +- vnpy/gateway/bitmex/bitmex_gateway.py | 15 +++-- vnpy/gateway/futu/futu_gateway.py | 6 +- vnpy/trader/constant.py | 11 +--- vnpy/trader/ui/widget.py | 42 ++++++++----- vnpy/trader/utility.py | 4 +- 15 files changed, 120 insertions(+), 113 deletions(-) delete mode 100644 .style.yapf diff --git a/.flake8 b/.flake8 index 21c53dec..6e607edb 100644 --- a/.flake8 +++ b/.flake8 @@ -6,3 +6,6 @@ ignore = W293 blank line contains whitespace W291 trailing whitespace + +[pycodestyle] +max_line_length = 79 \ No newline at end of file diff --git a/.style.yapf b/.style.yapf deleted file mode 100644 index 7321968c..00000000 --- a/.style.yapf +++ /dev/null @@ -1,15 +0,0 @@ -[style] -based_on_style = google -spaces_before_comment=2, 4 -SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED = true -SPLIT_ALL_COMMA_SEPARATED_VALUES = true -SPLIT_BEFORE_BITWISE_OPERATOR = true -SPLIT_BEFORE_CLOSING_BRACKET = true -SPLIT_BEFORE_DICT_SET_GENERATOR = true -SPLIT_BEFORE_DOT = true -SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN = true -SPLIT_BEFORE_FIRST_ARGUMENT = true -SPLIT_BEFORE_LOGICAL_OPERATOR = true -SPLIT_BEFORE_NAMED_ASSIGNS = true -SPLIT_COMPLEX_COMPREHENSION = true -DEDENT_CLOSING_BRACKETS = true diff --git a/README.md b/README.md index 98a15033..d40551b2 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,15 @@ vnpy 2.0 在提交代码的时候,请遵守以下规则,以提高代码质量: - * 使用[black]格式化你的代码。运行```black .```即可。 + * 使用[autopep8]格式化你的代码。运行```autopep8 --in-place --recursive . ```即可。 * 使用[pylint]检查你的代码(主要检查命名规则),确保没有error和warning。在项目根目录下运行```pylint vnpy```即可。 * 使用[flake8]检查你的代码,确保没有error和warning。在项目根目录下运行```flake8```即可。 - [yapf]:https://github.com/google/yapf + [autopep8]:https://github.com/hhatto/autopep8 [pylint]:https://github.com/PyCQA/pylint + [flake8]:https://pypi.org/project/flake8/ [提交PR]:https://help.github.com/articles/creating-a-pull-request/ - [创建 Issue]:http://pylint.pycqa.org/en/latest/tutorial.html + diff --git a/binding/generator/autocxxpy/cxxparser.py b/binding/generator/autocxxpy/cxxparser.py index 1a02e2fe..b0d7b345 100644 --- a/binding/generator/autocxxpy/cxxparser.py +++ b/binding/generator/autocxxpy/cxxparser.py @@ -154,9 +154,9 @@ class Method(Function): "virtual" if self.is_virtual else "", "static" if self.is_static else "", self.parent.name, - ) - + super().full_signature - + (" = 0" if self.is_pure_virtual else "") + ) + + super().full_signature + + (" = 0" if self.is_pure_virtual else "") ) def __str__(self): @@ -190,9 +190,9 @@ class CXXParser: args=self.args, unsaved_files=self.unsaved_files, options=( - TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD - | TranslationUnit.PARSE_SKIP_FUNCTION_BODIES - | TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION + TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD | + TranslationUnit.PARSE_SKIP_FUNCTION_BODIES | + TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION ), ) result = CXXParseResult() @@ -205,8 +205,8 @@ class CXXParser: e = CXXParser._process_enum(c) result.enums[e.name] = e elif ( - c.kind == CursorKind.CLASS_DECL - or c.kind == CursorKind.STRUCT_DECL + c.kind == CursorKind.CLASS_DECL or + c.kind == CursorKind.STRUCT_DECL ): class_ = CXXParser._process_class(c) cname = class_.name @@ -222,30 +222,30 @@ class CXXParser: name, definition = CXXParser._process_macro_definition(c) result.macros[name] = definition elif ( - False - or c.kind == CursorKind.ENUM_CONSTANT_DECL - or c.kind == CursorKind.CXX_METHOD - or c.kind == CursorKind.CXX_FINAL_ATTR - or c.kind == CursorKind.DESTRUCTOR - or c.kind == CursorKind.PARM_DECL - or c.kind == CursorKind.CXX_ACCESS_SPEC_DECL - or c.kind == CursorKind.FIELD_DECL + False or + c.kind == CursorKind.ENUM_CONSTANT_DECL or + c.kind == CursorKind.CXX_METHOD or + c.kind == CursorKind.CXX_FINAL_ATTR or + c.kind == CursorKind.DESTRUCTOR or + c.kind == CursorKind.PARM_DECL or + c.kind == CursorKind.CXX_ACCESS_SPEC_DECL or + c.kind == CursorKind.FIELD_DECL ): pass elif c.kind == CursorKind.COMPOUND_STMT: # ignore any body pass elif ( - CXXParser._is_literal_cursor(c) - or c.kind == CursorKind.MACRO_INSTANTIATION - or c.kind == CursorKind.INCLUSION_DIRECTIVE + CXXParser._is_literal_cursor(c) or + c.kind == CursorKind.MACRO_INSTANTIATION or + c.kind == CursorKind.INCLUSION_DIRECTIVE ): # just not need to process pass elif ( - c.kind == CursorKind.TYPE_REF - or c.kind == CursorKind.UNEXPOSED_EXPR - or c.kind == CursorKind.TRANSLATION_UNIT + c.kind == CursorKind.TYPE_REF or + c.kind == CursorKind.UNEXPOSED_EXPR or + c.kind == CursorKind.TRANSLATION_UNIT ): # i don't know what those are pass diff --git a/binding/generator/autocxxpy/preprocessor.py b/binding/generator/autocxxpy/preprocessor.py index 2c8dee90..c7274c84 100644 --- a/binding/generator/autocxxpy/preprocessor.py +++ b/binding/generator/autocxxpy/preprocessor.py @@ -71,7 +71,8 @@ class PreprocessedClass(Class): default_factory=(lambda: defaultdict(list)) ) need_wrap: bool = False # if need_wrap is true, wrap this to dict - is_pure_virtual: bool = False # generator will not assign python constructor for pure virtual + # generator will not assign python constructor for pure virtual + is_pure_virtual: bool = False class PreProcessorResult: @@ -158,8 +159,8 @@ class PreProcessor: # array of basic type, such as int[], char[] if ( - is_array_type(basic_combination) - and array_base(basic_combination) in base_types + is_array_type(basic_combination) and + array_base(basic_combination) in base_types ): return True diff --git a/vnpy/api/rest/rest_client.py b/vnpy/api/rest/rest_client.py index 625c590f..ff8de574 100644 --- a/vnpy/api/rest/rest_client.py +++ b/vnpy/api/rest/rest_client.py @@ -78,7 +78,7 @@ class Request(object): class RestClient(object): """ HTTP Client designed for all sorts of trading RESTFul API. - + * Reimplement before_request function to add signature function. * Reimplement on_failed function to handle Non-2xx responses. * Use on_failed parameter in add_request function for individual Non-2xx response handling. diff --git a/vnpy/api/websocket/websocket_client.py b/vnpy/api/websocket/websocket_client.py index 4a1fb3fe..61541594 100644 --- a/vnpy/api/websocket/websocket_client.py +++ b/vnpy/api/websocket/websocket_client.py @@ -14,7 +14,7 @@ import websocket class WebsocketClient(object): """ Websocket API - + After creating the client object, use start() to run worker and ping threads. The worker thread connects websocket automatically. @@ -28,7 +28,7 @@ class WebsocketClient(object): * on_disconnected * on_packet * on_error - + After start() is called, the ping thread will ping server every 60 seconds. """ @@ -76,7 +76,7 @@ class WebsocketClient(object): def stop(self): """ Stop the client. - + This function cannot be called from worker thread or callback function. """ self._active = False diff --git a/vnpy/app/cta_strategy/backtesting.py b/vnpy/app/cta_strategy/backtesting.py index 210c3364..1326a2a7 100644 --- a/vnpy/app/cta_strategy/backtesting.py +++ b/vnpy/app/cta_strategy/backtesting.py @@ -62,7 +62,7 @@ class OptimizationSetting: value += step self.params[name] = value_list - + def set_target(self, target: str): """""" self.target = target @@ -77,7 +77,7 @@ class OptimizationSetting: for p in products: setting = dict(zip(keys, p)) settings.append(setting) - + return settings @@ -179,7 +179,7 @@ class BacktestingEngine: if capital: self.capital = capital - + if end: self.end = end @@ -201,10 +201,10 @@ class BacktestingEngine: s = ( DbBarData.select() .where( - (DbBarData.vt_symbol == self.vt_symbol) - & (DbBarData.interval == self.interval) - & (DbBarData.datetime >= self.start) - & (DbBarData.datetime <= self.end) + (DbBarData.vt_symbol == self.vt_symbol) & + (DbBarData.interval == self.interval) & + (DbBarData.datetime >= self.start) & + (DbBarData.datetime <= self.end) ) .order_by(DbBarData.datetime) ) @@ -212,9 +212,9 @@ class BacktestingEngine: s = ( DbTickData.select() .where( - (DbTickData.vt_symbol == self.vt_symbol) - & (DbTickData.datetime >= self.start) - & (DbTickData.datetime <= self.end) + (DbTickData.vt_symbol == self.vt_symbol) & + (DbTickData.datetime >= self.start) & + (DbTickData.datetime <= self.end) ) .order_by(DbTickData.datetime) ) @@ -307,7 +307,8 @@ class BacktestingEngine: 0 ) df["highlevel"] = ( - df["balance"].rolling(min_periods=1, window=len(df), center=False).max() + df["balance"].rolling( + min_periods=1, window=len(df), center=False).max() ) df["drawdown"] = df["balance"] - df["highlevel"] df["ddpercent"] = df["drawdown"] / df["highlevel"] * 100 @@ -435,7 +436,7 @@ class BacktestingEngine: df["net_pnl"].hist(bins=50) plt.show() - + def run_optimization(self, optimization_setting: OptimizationSetting): """""" # Get optimization setting and target @@ -445,7 +446,7 @@ class BacktestingEngine: if not settings: self.output("优化参数组合为空,请检查") return - + if not target_name: self.output("优化目标为设置,请检查") return @@ -456,10 +457,10 @@ class BacktestingEngine: results = [] for setting in settings: result = (pool.apply_async(optimize, ( - target_name, - self.strategy_class, - setting, - self.vt_symbol, + target_name, + self.strategy_class, + setting, + self.vt_symbol, self.interval, self.start, self.rate, @@ -540,15 +541,15 @@ class BacktestingEngine: # Check whether limit orders can be filled. long_cross = ( - order.direction == Direction.LONG - and order.price >= long_cross_price - and long_cross_price > 0 + order.direction == Direction.LONG and + order.price >= long_cross_price and + long_cross_price > 0 ) short_cross = ( - order.direction == Direction.SHORT - and order.price <= short_cross_price - and short_cross_price > 0 + order.direction == Direction.SHORT and + order.price <= short_cross_price and + short_cross_price > 0 ) if not long_cross and not short_cross: @@ -608,13 +609,13 @@ class BacktestingEngine: for stop_order in list(self.active_stop_orders.values()): # Check whether stop order can be triggered. long_cross = ( - stop_order.direction == Direction.LONG - and stop_order.price <= long_cross_price + stop_order.direction == Direction.LONG and + stop_order.price <= long_cross_price ) short_cross = ( - stop_order.direction == Direction.SHORT - and stop_order.price >= short_cross_price + stop_order.direction == Direction.SHORT and + stop_order.price >= short_cross_price ) if not long_cross and not short_cross: @@ -848,7 +849,8 @@ class DailyResult: # Holding pnl is the pnl from holding position at day start self.start_pos = start_pos self.end_pos = start_pos - self.holding_pnl = self.start_pos * (self.close_price - self.pre_close) * size + self.holding_pnl = self.start_pos * \ + (self.close_price - self.pre_close) * size # Trading pnl is the pnl from new trade during the day self.trade_count = len(self.trades) @@ -861,7 +863,8 @@ class DailyResult: turnover = trade.price * trade.volume * size - self.trading_pnl += pos_change * (self.close_price - trade.price) * size + self.trading_pnl += pos_change * \ + (self.close_price - trade.price) * size self.end_pos += pos_change self.turnover += turnover self.commission += turnover * rate @@ -874,7 +877,7 @@ class DailyResult: def optimize( target_name: str, - strategy_class: CtaTemplate, + strategy_class: CtaTemplate, setting: dict, vt_symbol: str, interval: Interval, @@ -903,12 +906,12 @@ def optimize( end=end, mode=mode ) - + engine.add_strategy(strategy_class, setting) engine.load_data() engine.run_backtesting() engine.calculate_result() statistics = engine.calculate_statistics() - + target_value = statistics[target_name] return (str(setting), target_value, statistics) diff --git a/vnpy/app/cta_strategy/ui/widget.py b/vnpy/app/cta_strategy/ui/widget.py index f73e0b93..b4fc736c 100644 --- a/vnpy/app/cta_strategy/ui/widget.py +++ b/vnpy/app/cta_strategy/ui/widget.py @@ -68,8 +68,6 @@ class CtaManager(QtWidgets.QWidget): scroll_area.setWidgetResizable(True) scroll_area.setWidget(scroll_widget) - # bottom_height = 300 - self.log_monitor = LogMonitor(self.main_engine, self.event_engine) self.stop_order_monitor = StopOrderMonitor( diff --git a/vnpy/event/engine.py b/vnpy/event/engine.py index b7af2dec..c2184023 100644 --- a/vnpy/event/engine.py +++ b/vnpy/event/engine.py @@ -65,7 +65,7 @@ class EventEngine: """ First ditribute event to those handlers registered listening to this type. - + Then distrubute event to those general handlers which listens to all types. """ diff --git a/vnpy/gateway/bitmex/bitmex_gateway.py b/vnpy/gateway/bitmex/bitmex_gateway.py index c6c1ad10..263fa02c 100644 --- a/vnpy/gateway/bitmex/bitmex_gateway.py +++ b/vnpy/gateway/bitmex/bitmex_gateway.py @@ -86,7 +86,8 @@ class BitmexGateway(BaseGateway): proxy_host = setting["proxy_host"] proxy_port = setting["proxy_port"] - self.rest_api.connect(key, secret, session, server, proxy_host, proxy_port) + self.rest_api.connect(key, secret, session, + server, proxy_host, proxy_port) self.ws_api.connect(key, secret, server, proxy_host, proxy_port) @@ -407,7 +408,8 @@ class BitmexWebsocketApi(WebsocketClient): msg = f"触发异常,状态码:{exception_type},信息:{exception_value}" self.gateway.write_log(msg) - sys.stderr.write(self.exception_detail(exception_type, exception_value, tb)) + sys.stderr.write(self.exception_detail( + exception_type, exception_value, tb)) def authenticate(self): """ @@ -450,7 +452,8 @@ class BitmexWebsocketApi(WebsocketClient): return tick.last_price = d["price"] - tick.datetime = datetime.strptime(d["timestamp"], "%Y-%m-%dT%H:%M:%S.%fZ") + tick.datetime = datetime.strptime( + d["timestamp"], "%Y-%m-%dT%H:%M:%S.%fZ") self.gateway.on_tick(copy(tick)) def on_depth(self, d): @@ -470,7 +473,8 @@ class BitmexWebsocketApi(WebsocketClient): tick.__setattr__("ask_price_%s" % (n + 1), price) tick.__setattr__("ask_volume_%s" % (n + 1), volume) - tick.datetime = datetime.strptime(d["timestamp"], "%Y-%m-%dT%H:%M:%S.%fZ") + tick.datetime = datetime.strptime( + d["timestamp"], "%Y-%m-%dT%H:%M:%S.%fZ") self.gateway.on_tick(copy(tick)) def on_trade(self, d): @@ -552,7 +556,8 @@ class BitmexWebsocketApi(WebsocketClient): accountid = str(d["account"]) account = self.accounts.get(accountid, None) if not account: - account = AccountData(accountid=accountid, gateway_name=self.gateway_name) + account = AccountData(accountid=accountid, + gateway_name=self.gateway_name) self.accounts[accountid] = account account.balance = d.get("marginBalance", account.balance) diff --git a/vnpy/gateway/futu/futu_gateway.py b/vnpy/gateway/futu/futu_gateway.py index 0eac4ca8..194a550d 100644 --- a/vnpy/gateway/futu/futu_gateway.py +++ b/vnpy/gateway/futu/futu_gateway.py @@ -322,7 +322,8 @@ class FutuGateway(BaseGateway): account = AccountData( accountid=f"{self.gateway_name}_{self.market}", balance=float(row["total_assets"]), - frozen=(float(row["total_assets"]) - float(row["avl_withdrawal_cash"])), + frozen=(float(row["total_assets"]) - + float(row["avl_withdrawal_cash"])), gateway_name=self.gateway_name, ) self.on_account(account) @@ -412,7 +413,8 @@ class FutuGateway(BaseGateway): date = row["data_date"].replace("-", "") time = row["data_time"] - tick.datetime = datetime.strptime(f"{date} {time}", "%Y%m%d %H:%M:%S") + tick.datetime = datetime.strptime( + f"{date} {time}", "%Y%m%d %H:%M:%S") tick.open_price = row["open_price"] tick.high_price = row["high_price"] tick.low_price = row["low_price"] diff --git a/vnpy/trader/constant.py b/vnpy/trader/constant.py index c68105aa..289607c2 100644 --- a/vnpy/trader/constant.py +++ b/vnpy/trader/constant.py @@ -9,7 +9,6 @@ class Direction(Enum): """ Direction of order/trade/position. """ - LONG = "多" SHORT = "空" NET = "净" @@ -19,7 +18,6 @@ class Offset(Enum): """ Offset of order/trade. """ - NONE = "" OPEN = "开" CLOSE = "平" @@ -31,7 +29,6 @@ class Status(Enum): """ Order status. """ - SUBMITTING = "提交中" NOTTRADED = "未成交" PARTTRADED = "部分成交" @@ -44,7 +41,6 @@ class Product(Enum): """ Product class. """ - EQUITY = "股票" FUTURES = "期货" OPTION = "期权" @@ -60,7 +56,6 @@ class PriceType(Enum): """ Order price type. """ - LIMIT = "限价" MARKET = "市价" FAK = "FAK" @@ -71,7 +66,6 @@ class OptionType(Enum): """ Option type. """ - CALL = "看涨期权" PUT = "看跌期权" @@ -80,7 +74,6 @@ class Exchange(Enum): """ Exchange. """ - # Chinese CFFEX = "CFFEX" SHFE = "SHFE" @@ -109,13 +102,15 @@ class Currency(Enum): """ Currency. """ - USD = "USD" HKD = "HKD" CNY = "CNY" class Interval(Enum): + """ + Interval of bar data. + """ MINUTE = "1m" HOUR = "1h" DAILY = "d" diff --git a/vnpy/trader/ui/widget.py b/vnpy/trader/ui/widget.py index d0f84a94..fa647913 100644 --- a/vnpy/trader/ui/widget.py +++ b/vnpy/trader/ui/widget.py @@ -304,7 +304,8 @@ class BaseMonitor(QtWidgets.QTableWidget): """ Save table data into a csv file """ - path, _ = QtWidgets.QFileDialog.getSaveFileName(self, "保存数据", "", "CSV(*.csv)") + path, _ = QtWidgets.QFileDialog.getSaveFileName( + self, "保存数据", "", "CSV(*.csv)") if not path: return @@ -499,7 +500,8 @@ class ConnectDialog(QtWidgets.QDialog): self.setWindowTitle(f"连接{self.gateway_name}") # Default setting provides field name, field data type and field default value. - default_setting = self.main_engine.get_default_setting(self.gateway_name) + default_setting = self.main_engine.get_default_setting( + self.gateway_name) # Saved setting provides field data used last time. loaded_setting = load_setting(self.filename) @@ -588,13 +590,15 @@ class TradingWidget(QtWidgets.QWidget): self.name_line.setReadOnly(True) self.direction_combo = QtWidgets.QComboBox() - self.direction_combo.addItems([Direction.LONG.value, Direction.SHORT.value]) + self.direction_combo.addItems( + [Direction.LONG.value, Direction.SHORT.value]) self.offset_combo = QtWidgets.QComboBox() self.offset_combo.addItems([offset.value for offset in Offset]) self.price_type_combo = QtWidgets.QComboBox() - self.price_type_combo.addItems([price_type.value for price_type in PriceType]) + self.price_type_combo.addItems( + [price_type.value for price_type in PriceType]) double_validator = QtGui.QDoubleValidator() double_validator.setBottom(0) @@ -637,11 +641,16 @@ class TradingWidget(QtWidgets.QWidget): self.bp4_label = self.create_label(bid_color) self.bp5_label = self.create_label(bid_color) - self.bv1_label = self.create_label(bid_color, alignment=QtCore.Qt.AlignRight) - self.bv2_label = self.create_label(bid_color, alignment=QtCore.Qt.AlignRight) - self.bv3_label = self.create_label(bid_color, alignment=QtCore.Qt.AlignRight) - self.bv4_label = self.create_label(bid_color, alignment=QtCore.Qt.AlignRight) - self.bv5_label = self.create_label(bid_color, alignment=QtCore.Qt.AlignRight) + self.bv1_label = self.create_label( + bid_color, alignment=QtCore.Qt.AlignRight) + self.bv2_label = self.create_label( + bid_color, alignment=QtCore.Qt.AlignRight) + self.bv3_label = self.create_label( + bid_color, alignment=QtCore.Qt.AlignRight) + self.bv4_label = self.create_label( + bid_color, alignment=QtCore.Qt.AlignRight) + self.bv5_label = self.create_label( + bid_color, alignment=QtCore.Qt.AlignRight) self.ap1_label = self.create_label(ask_color) self.ap2_label = self.create_label(ask_color) @@ -649,11 +658,16 @@ class TradingWidget(QtWidgets.QWidget): self.ap4_label = self.create_label(ask_color) self.ap5_label = self.create_label(ask_color) - self.av1_label = self.create_label(ask_color, alignment=QtCore.Qt.AlignRight) - self.av2_label = self.create_label(ask_color, alignment=QtCore.Qt.AlignRight) - self.av3_label = self.create_label(ask_color, alignment=QtCore.Qt.AlignRight) - self.av4_label = self.create_label(ask_color, alignment=QtCore.Qt.AlignRight) - self.av5_label = self.create_label(ask_color, alignment=QtCore.Qt.AlignRight) + self.av1_label = self.create_label( + ask_color, alignment=QtCore.Qt.AlignRight) + self.av2_label = self.create_label( + ask_color, alignment=QtCore.Qt.AlignRight) + self.av3_label = self.create_label( + ask_color, alignment=QtCore.Qt.AlignRight) + self.av4_label = self.create_label( + ask_color, alignment=QtCore.Qt.AlignRight) + self.av5_label = self.create_label( + ask_color, alignment=QtCore.Qt.AlignRight) self.lp_label = self.create_label() self.return_label = self.create_label(alignment=QtCore.Qt.AlignRight) diff --git a/vnpy/trader/utility.py b/vnpy/trader/utility.py index 0a8198a1..8595457c 100644 --- a/vnpy/trader/utility.py +++ b/vnpy/trader/utility.py @@ -15,10 +15,10 @@ from .object import BarData, TickData class Singleton(type): """ Singleton metaclass, - + class A: __metaclass__ = Singleton - + """ _instances = {}