How to assign the output of the print function (or any function) to a variable?
To give an example:
import eyeD3
tag = eyeD3.Tag()
tag.link("/some/file.mp3")
print tag.getArtist()
How do I assign the output of print tag.getArtist to a variable?
The print statement in Python converts its arguments to strings, and outputs those strings to stdout. To save the string to a variable instead, only convert it to a string:
a = str(tag.getArtist())
To answer the question more generaly how to redirect standard output to a variable ?
do the following :
from io import StringIO
import sys
result = StringIO()
sys.stdout = result
result_string = result.getvalue()
If you need to do that only in some function do the following :
old_stdout = sys.stdout
# your function containing the previous lines
my_function()
sys.stdout = old_stdout
probably you need one of str,repr or unicode functions
somevar = str(tag.getArtist())
depending which python shell are you using
You can use parameter file to redirect output of print function
from io import StringIO
s = StringIO()
print(42, file=s)
result = s.getvalue()
somevar = tag.getArtist()
http://docs.python.org/tutorial/index.html
This is a standalone example showing how to save the output of a user-written function in Python 3:
from io import StringIO
import sys
def print_audio_tagging_result(value):
print(f"value = {value}")
tag_list = []
for i in range(0,1):
save_stdout = sys.stdout
result = StringIO()
sys.stdout = result
print_audio_tagging_result(i)
sys.stdout = save_stdout
tag_list.append(result.getvalue())
print(tag_list)
Output
['value = 0\n']
In Python 3.x, you can assign print() statement to the variable like this:
>>> var = print('some text')
some text
>>> var
>>> type(var)
<class 'NoneType'>
According to the documentation,
All non-keyword arguments are converted to strings like str() does and written to the stream, separated by sep and followed by end. Both sep and end must be strings; they can also be None, which means to use the default values. If no objects are given, print() will just write end.
The file argument must be an object with a write(string) method; if it is not present or None, sys.stdout will be used. Since printed arguments are converted to text strings, print() cannot be used with binary mode file objects. For these, use file.write(...) instead.
That's why we cannot assign print() statement values to the variable. In this question you have ask (or any function). So print() also a function with the return value with None. So the return value of python function is None. But you can call the function(with parenthesis ()) and save the return value in this way.
>>> var = some_function()
So the var variable has the return value of some_function() or the default value None. According to the documentation about print(), All non-keyword arguments are converted to strings like str() does and written to the stream. Lets look what happen inside the str().
Return a string version of object. If object is not provided, returns the empty string. Otherwise, the behavior of str() depends on whether encoding or errors is given, as follows.
So we get a string object, then you can modify the below code line as follows,
>>> var = str(some_function())
or you can use str.join() if you really have a string object.
Return a string which is the concatenation of the strings in iterable. A TypeError will be raised if there are any non-string values in iterable, including bytes objects. The separator between elements is the string providing this method.
change can be as follows,
>>> var = ''.join(some_function()) # you can use this if some_function() really returns a string value
Related
I have a program which goes as follows
data = bytes()
...
new_data = bytes()
...
data += new_data
print(data)
and so I have mocked bytes accordingly along with the __iadd__ call as follows:
#unittest.mock.patch('file_name.bytes')
def test(self, mock_bytes):
mock_bytes.__iadd__.return_value = mock_bytes
...
From my understanding, a MagicMock of bytes() should be returned, but instead I get a MagicMock of bytes().__iadd__().
I'd appreciate any help on the matter :)
P.S. have also tried variations like mock_bytes(), mock_bytes().__iadd__(), or any other variation.
In short, the mock works as expected. To override data += new_data you should have this syntax: mock_bytes.return_value.__iadd__.return_value = "the value which is printed".
A note about mock syntax: you can often replace .return_value with (), so mock_bytes.return_value.__iadd__.return_value = "the value which is printed" is equivalent to mock_bytes().__iadd__.return_value = "the value which is printed"
Now, as to why you need the () / .return_value in the first place:
Mocks are tied to the implementation - they mimic the functions called. So if you want to change the value of data variable in this line: data = bytes() you are changing the return value of the function bytes. You were right to identify that iadd is called, and once more, you want to change the return value.
Then you have concatenation/chaining of calls: you are calling bytes, then apply iadd on the returned object. So you have this syntax: mock_bytes.return_value.__iadd__.return_value = "the value which is printed"
I'm not sure why I'm seeing this error message: AttributeError: 'generator' object has no attribute 'replace' (on line: modified_file = hex_read_file.replace(batch_to_amend_final, batch_amendment_final).
import binascii, os, re, time
os.chdir(...)
files_to_amend = os.listdir(...)
joiner = "00"
# Allow user to input the text to be replaced, and with what
while True:
batch_to_amend3 = input("\n\nWhat number would you like to amend? \n\n >>> ")
batch_amendment3 = input("\n\nWhat is the new number? \n\n >>> ")
batch_to_amend2 = batch_to_amend3.encode()
batch_to_amend = joiner.encode().join(binascii.hexlify(bytes((i,))) for i in batch_to_amend2)
batch_amendment2 = batch_amendment3.encode()
batch_amendment = joiner.encode().join(binascii.hexlify(bytes((i,))) for i in batch_amendment2)
# Function to translate label files
def lbl_translate(files_to_amend):
with open(files_to_amend, 'rb') as read_file:
read_file2 = read_file.read()
hex_read_file = (binascii.hexlify(bytes((i,))) for i in read_file2)
print(hex_read_file)
modified_file = hex_read_file.replace(batch_to_amend, batch_amendment)
with open(files_to_amend, 'wb') as write_file:
write_file.write(modified_file)
write_file.close()
print("Amended: " + files_to_amend)
# Calling function to modify labels
for label in files_to_amend:
lbl_translate(label)
hex_read_file is a generator comprehension (note the round brackets around the statement) defined here:
hex_read_file = (binascii.hexlify(bytes((i,))) for i in read_file2)
As many already pointed out in the comments, comprehesions don't have a replace method as strings have, so you have two possibilities, depending on your specific use-case:
Turn the comprehension in a bytestring and call replace on that (considering how you use write_file.write(modified_file) afterwards, this is the option that would work with that directly):
hex_read_file = bytes(binascii.hexlify(bytes((int(i),))) for i in read_file2) # note: I added th eadditional int() call to fix the issue mentioned in the comments
Filter and replace directly in the comprehension (and modify how you write out the result):
def lbl_translate(files_to_amend, replacement_map):
with open(files_to_amend, 'rb') as read_file:
read_file2 = read_file.read()
hex_read_file = ( replacement_map.get(binascii.hexlify(bytes((int(i),))), binascii.hexlify(bytes((int(i),)))) for i in read_file2) # see Note below
with open(files_to_amend, 'wb') as write_file:
for b in hex_read_file:
write_file.write(b)
print("Amended: " + files_to_amend)
where replacement_map is a dict that you fill in with the batch_to_amend as key and the batch_amendment value (you can speficy multiple amendments too and it will work just the same). The call would then be:
for label in files_to_amend:
lbl_translate(label,{batch_to_amend:batch_amendment})
NOTE: Using standard python dicts, because of how comprehensions work, you need to call binascii.hexlify(bytes((int(i),))) twice here. A better option uses collections.defaultdict
A better option would use defaultdict, if they were implemented in a sensible way (see here for more context on why I say that). defaltdicts expect a lambda with no parameters generating the value for unknown keys, instead you need to create your own subclass of dict and implement the __missing__ method to obtain the desired behaviour:
hex_read_file = ( replacement_map[binascii.hexlify(bytes((int(i),)))] for i in read_file2) # replacement_map is a collections.defaultdict
and you define replacement_map as:
class dict_with_key_as_default(dict): # find a better name for the type
def __missing__(self, key):
'''if a value is not in the dictionary, return the key value instead.'''
return key
replacement_map = dict_with_key_as_default()
replacement_map[batch_to_amend] = batch_amendment
for label in files_to_amend:
lbl_translate(label, replacement_map)
(class dict_with_key_as_default taken from this answer and renamed for clarity)
Edit note: As mentioned in the comments, the OP has an error in the comprehension where they call hexlify() on some binary string instead of integer values. The solution adds a cast to int for the bytes where relevant, but it's far from the best solution to this problem. Since the OP's intent is not clear, I left it as close to the original as possible, but an alternative solution should be used instead.
I have a content of a function given by a string. I'm looking for a way how to convert this string into a function object to call it.
content = "def my_function(x):\n return x**2"
my_function = extract_function_from_string(content) # extract_function_from_string ???
print my_function(5) # prints 25
Perhaps, there's a way to convert the string into a python module and extract the function from it
If you know the function name, you can run exec on the string. Eval will not work as it only targets expressions. See \r\n vs \n in python eval function. If you don't know the function name, but all your function have the same format you can try something like this:
def extract_function_from_string(content):
exec(content)
start = 4
end = content.find("(")
function_name = content[start:end]
return eval(function_name)
my_function = extract_function_from_string("def my_function(x):\n return x**2")
print my_function(5)
The train has left the safe and sound python station a long time ago, but I guess this is just a curiosity.
You can use exec
>>>content = "def my_function(x):\n return x**2"
>>>exec content
>>>my_function(5)
25
For Python 3.x
>>>exec(content)
Try the exec statement:
>>> content = "def my_function(x):\n return x**2"
>>> exec content
>>> my_function(5)
25
Here is something that does not leave safe train of python completely.
def extract_function_from_string(content, function_index=0):
import ast
tree = ast.parse(content, mode='exec')
function = tree.body[function_index]
module = ast.Module([function])
exec(compile(module, filename="<ast>", mode='exec'))
return locals()[function.name]
content = "def my_function(x):\n return x**2"
my_function = extract_function_from_string(content) # extract_function_from_string ???
print(my_function(5)) # prints 25
You can use the eval function to do this. e.g. eval("print('Hello')").
However, do be advised that this can be a security risk, and there is probably a better way of doing it than parsing a string.
a = "lambda x:x**2"
my_func = eval(a)
print my_func(3)
I have the following code:
some_dict = {'a':0, 'b':1}
line = "some_dict_b = %s\n" % pickle.dumps(some_dict,2)
exec(line)
decoded_dict = pickle.loads(some_dict_b)
decoded_dict == some_dict
In python 3 this code prints True. In python 2 (2.7.8) I get an error in the exec line. I know dumps returns str in 2.7 while it returns a byte-stream in 3.
I am writing a program that parses data from an input file then creates certain memory objects and should write out a python script that uses these objects. I write these objects in the script file using pickle.dumps() and inserting it into a variable declaration line as per the idea sketched above. But I need to be able to run this code in python 2.
I did notice that in python 3 the line variable gets each backslash properly escaped and a type:
>>> line
"some_dict_b = b'\\x80\\x02...
while in python 2 I get:
>>> line
'some_dict_b = \x80\x02...
The Python 3 bytes type doesn't have a string represention, so when converted to a string with %s, the object representation is used instead. If you wanted to produce Python-compatible syntax from objects, you can use the %r formatter instead, to just use the representation directly.
In Python 2:
>>> import pickle
>>> some_dict = {'a':0, 'b':1}
>>> p = pickle.dumps(some_dict, 2)
>>> print 'string: %s\nrepresentation: %r' % (p, p)
string: ?}q(UaqKUbqKu.
representation: '\x80\x02}q\x00(U\x01aq\x01K\x00U\x01bq\x02K\x01u.'
In Python 3:
>>> import pickle
>>> some_dict = {'a':0, 'b':1}
>>> p = pickle.dumps(some_dict, 2)
>>> print('string: %s\nrepresentation: %r' % (p, p))
string: b'\x80\x02}q\x00(X\x01\x00\x00\x00bq\x01K\x01X\x01\x00\x00\x00aq\x02K\x00u.'
representation: b'\x80\x02}q\x00(X\x01\x00\x00\x00bq\x01K\x01X\x01\x00\x00\x00aq\x02K\x00u.'
Object representations (the output of the repr() function, which uses the object.__repr__ special method) generally will attempt to provide you with a representation that can be pasted back into a Python script or interactive prompt to recreate the same value.
From the documentation for repr():
For many types, this function makes an attempt to return a string that would yield an object with the same value when passed to eval(), otherwise the representation is a string enclosed in angle brackets that contains the name of the type of the object together with additional information often including the name and address of the object.
None of this is specific to pickle, really.
Whenever you think "I use exec", think again. You don't. Instead of evaluating data like this, store the contents of the data inside a dict itself.
Then, assign the data explicit to the variable.
some_dict = {'a':0, 'b':1}
line = pickle.dumps(some_dict)
decoded_dict = pickle.loads(line)
decoded_dict == some_dict
You can call repr on the string or bytes object before inserting them into the line.
# Python 2
>>> 'some_dict = %s' % repr(pickle.dumps(d))
'some_dict = "(dp0\\nS\'a\'\\np1\\nI12\\nsS\'b\'\\np2\\nI24\\ns."'
# Python 3
>>> 'some_dict = %s' % repr(pickle.dumps(d))
"some_dict = b'\\x80\\x03}q\\x00(X\\x01\\x00\\x00\\x00bq\\x01K\\x18X\\x01\\x00\\x00\\x00aq\\x02K\\x0cu.'"
Or use the format method, using !r to automatically call repr:
>>> 'some_dict = {!r}'.format(pickle.dumps(d))
"some_dict = b'\\x80\\x03}q\\x00(X\\x01\\x00\\x00\\x00bq\\x01K\\x18X\\x01\\x00\\x00\\x00aq\\x02K\\x0cu.'"
(Also works in python 2)
I am trying to make a decrypter that decrypts code from the encrypter I made. I am getting this type error when I run the code though
getcrypt = ''.join(map(Decrypt.get,split_up_into_sixteen_chars(x_str)))
TypeError: split_up_into_sixteen_cjars() takes 0 positional arguments but 1 was given
I'm fairly new to programming and not sure whats causing this.
heres my code
Decrypt = {'1s25FF5ML10IF7aC' : 'A', 1s2afF5ML10I7ac' : 'a'} #I obviously have more than this but I'm trying to make it as simplified as possible
def split_up_into_sixteen_chars():
while len(x_str)>0:
v = x_str[:16]
print(v)
x_str = (input())
getcrypt = ''.join(map(Decrypt.get,split_up_into_sixteen_chars(x_str)))
print(getcrypt)
You have defined a function that takes no parameters:
def split_up_into_sixteen_chars():
yet you are passing it one:
split_up_into_sixteen_chars(x_str)
You need to tell Python that the function takes one parameter here, and name it:
def split_up_into_sixteen_chars(x_str):
The name used does not have to match the name that you pass in for the function call, but it does have to match what you use inside the function. The following function would also work; all I did was rename the parameter:
def split_up_into_sixteen_chars(some_string):
while len(some_string) > 0:
v = some_string[:16]
print(v)
This works because the parameter some_string becomes a local name, local to the function. It only exists inside of the function, and is gone again once the function completes.
Note that your function creates an infinite loop; the length of some_string will either always be 0, or always be longer than 0. The length does not change in the body of the loop.
The following would work better:
def split_up_into_sixteen_chars(some_string):
while len(some_string) > 0:
v = some_string[:16]
print(v)
some_string = some_string[16:]
because then we replace some_string with a shorter version of itself each time.
Your next problem is that the function doesn't return anything; Python then takes a default return value of None. Printing is something else entirely, print() writes the data to your console or IDE, but the caller of the function does not get to read that information.
In this case, you really want a generator function, and use yield. Generator functions return information in chunks; you can ask a generator for the next chunk one by one, and that is exactly what map() would do. Change the function to:
def split_up_into_sixteen_chars(some_string):
while len(some_string) > 0:
v = some_string[:16]
yield v
some_string = some_string[16:]
or even:
def split_up_into_sixteen_chars(some_string):
while some_string:
yield some_string[:16]
some_string = some_string[16:]
because an empty string is 'false-y' when it comes to boolean tests as used by while and if.
As your map(Decrypt.get, ...) stands, if split_up_into_sixteen_chars() yields anything that is not present as a key in Dycrypt, a None is produced (the default value for dict.get() if the key is not there), and ''.join() won't like that. The latter method can only handle strings.
One option would be to return a string default instead:
''.join(map(lambda chunk: Decrypt.get(chunk, ''), split_up_into_sixteen_chars(x_str)))
Now '', the empty string, is returned for chunks that are not present in Decrypt. This makes the whole script work for whatever string input you have:
>>> x_str='Hello world!'
>>> ''.join(map(lambda chunk: Decrypt.get(chunk, ''), split_up_into_sixteen_chars(x_str)))
''
>>> x_str = '1s25FF5ML10IF7aC'
>>> ''.join(map(lambda chunk: Decrypt.get(chunk, ''), split_up_into_sixteen_chars(x_str)))
'A'