Reputation: 65
I get the following error when trying to get information about a coins price, using the following code.
ticker = client.get_symbol_ticker(stock.symbol)
Also, here is the stock object.
class Stock:
def __init__(self, symbol):
self.symbol = symbol
self.alreadyHave = False
self.prices = []
self.priceBoughtAt = 0
self.quantityBought = 0
self.marketClosed = False
self.predictedPrices = []
# self.positiveTweets = 1
# self.negativeTweets = 0
Upvotes: 2
Views: 3213
Reputation: 881093
If you examine the relevant python-binance documentation, you'll see the following for that method call:
get_symbol_ticker(**params)
You need to realise that **blah_blah
requires keyword arguments rather than positional ones. Hence your code needs to do this instead:
ticker = client.get_symbol_ticker(symbol=stock.symbol)
# ^^^^^^^
# make keyword argument
That's the simple fix, now prepare to be educated :-)
The reason why this in necessary is that, when a Python function takes a **kwargs
parameter (as a lot of stuff in the Binance API does), it automagically constructs a dictionary for you based on the keyword arguments that you haven't explicitly extracted.
And therein lies the crux, they must be keyword arguments. Without that, there's no way for Python to know the key to use in the dictionary.
Consider the following code, built to emulate a method call (with self
), even though it's not necessary here:
def fn(self, **kwargs):
print(f"self='{self}', kwargs='{kwargs}'")
fn(42, x=7, y=9)
print('-----')
fn(42, 7, 9)
When you run this, you'll see:
self='42', kwargs='{'x': 7, 'y': 9}'
-----
Traceback (most recent call last):
File "prog.py", line 6, in <module>
fn(42, 7, 9)
TypeError: fn() takes 1 positional argument but 3 were given
You can see in the first (good) call that it constructs a dictionary for you based on the names and values. In the second (bad) call, there are no keyword arguments, so it assumes they're real parameters, hence the mismatch on argument count.
And be aware that there is an equivalent positional-argument variant *args
. The name is irrelevant in both cases, only the number of asterisks is important but, like self
(which could be me
, this
, or even the_object_you_would_commonly_reference_with_the_perpendicular_pronoun
), it's probably best to follow convention.
The basic idea is that Python collects all the specific arguments provided in the call, based on the definition. It then collects the unused positional arguments into a tuple, and the unused keyword arguments into a dictionary, and provides them if the definition has requested that.
The following code illustrates this (and also shows that the names of the arguments is irrelevant):
def fn(self, *pax, w, **paxkw):
print(f"self='{self}', w='{w}', pax='{pax}', paxkw='{paxkw}'")
print(f"pax type='{type(pax)}', paxkw type='{type(paxkw)}'")
fn(42, 7, 8, 9, 10, w=6, x=11, y=12, z=13)
and the output is:
self='42', w='6', pax='(7, 8, 9, 10)', paxkw='{'x': 11, 'y': 12, 'z': 13}'
pax type='<class 'tuple'>', paxkw type='<class 'dict'>'
There you can see that w
is not part of the keyword arguments, because you explicitly collected it in the definition (in the same way self
didn't become part of the tuple).
It's a very nifty way to allow more arguments to be passed than the minimum required. For an example of the tuple variant, you could provide a generic product
function which would multiply all its inputs, regardless of how many there are:
def prod(*items):
if len(items) == 0: return None
result = 1
for item in items:
result *= item
return result
print(prod()) # None
print(prod(7)) # 7
print(prod(1, 2, 3, 4, 5)) # 120
An example of the dictionary variant would be to allow use of arbitrarily-named items, not known in advance:
def any_fn(**items):
print('Called any_fn:')
for key in items:
print(f' [{key}] = "{items[key]}"')
any_fn()
any_fn(pi=3.14159, e=2.71828)
any_fn(pax='handsome')
The output of that is:
Called any_fn:
Called any_fn:
[pi] = "3.14159"
[e] = "2.71828"
Called any_fn:
[pax] = "handsome"
Upvotes: 0