itertools.product using list comprehension - python
I want to use list comprehension, instead of itertools.product
def pr(l):
return [''.join(i) for i in [(x,y) for x in l for y in l]]
operators = ['/','*','+','-']
pr(operators)
['//', '/*', '/+', '/-', '*/', '**', '*+', '*-', '+/', '+*', '++', '+-', '-/', '-*', '-+', '--']
This works, but I want to modify my function such that it returns combinations in pairs of repeat:
def pr(l, repeat=1):
# list comprehension code here
o = ['/','*','+','-']
pr(operators, repeat=4)
pr(operators, repeat=3)
['////', '///*', '///+', '///-', '//*/', '//**', '//*+', '//*-', '//+/', '//+*', '//++', '//+-', '//-/', '//-*', '//-+', '//--', '/*//', '/*/*', '/*/+', '/*/-', '/**/', '/***', '/**+', '/**-', '/*+/', '/*+*', '/*++', '/*+-', '/*-/', '/*-*', '/*-+', '/*--', '/+//', '/+/*', '/+/+', '/+/-', '/+*/', '/+**', '/+*+', '/+*-', '/++/', '/++*', '/+++', '/++-', '/+-/', '/+-*', '/+-+', '/+--', '/-//', '/-/*', '/-/+', '/-/-', '/-*/', '/-**', '/-*+', '/-*-', '/-+/', '/-+*', '/-++', '/-+-', '/--/', '/--*', '/--+', '/---', '*///', '*//*', '*//+', '*//-', '*/*/', '*/**', '*/*+', '*/*-', '*/+/', '*/+*', '*/++', '*/+-', '*/-/', '*/-*', '*/-+', '*/--', '**//', '**/*', '**/+', '**/-', '***/', '****', '***+', '***-', '**+/', '**+*', '**++', '**+-', '**-/', '**-*', '**-+', '**--', '*+//', '*+/*', '*+/+', '*+/-', '*+*/', '*+**', '*+*+', '*+*-', '*++/', '*++*', '*+++', '*++-', '*+-/', '*+-*', '*+-+', '*+--', '*-//', '*-/*', '*-/+', '*-/-', '*-*/', '*-**', '*-*+', '*-*-', '*-+/', '*-+*', '*-++', '*-+-', '*--/', '*--*', '*--+', '*---', '+///', '+//*', '+//+', '+//-', '+/*/', '+/**', '+/*+', '+/*-', '+/+/', '+/+*', '+/++', '+/+-', '+/-/', '+/-*', '+/-+', '+/--', '+*//', '+*/*', '+*/+', '+*/-', '+**/', '+***', '+**+', '+**-', '+*+/', '+*+*', '+*++', '+*+-', '+*-/', '+*-*', '+*-+', '+*--', '++//', '++/*', '++/+', '++/-', '++*/', '++**', '++*+', '++*-', '+++/', '+++*', '++++', '+++-', '++-/', '++-*', '++-+', '++--', '+-//', '+-/*', '+-/+', '+-/-', '+-*/', '+-**', '+-*+', '+-*-', '+-+/', '+-+*', '+-++', '+-+-', '+--/', '+--*', '+--+', '+---', '-///', '-//*', '-//+', '-//-', '-/*/', '-/**', '-/*+', '-/*-', '-/+/', '-/+*', '-/++', '-/+-', '-/-/', '-/-*', '-/-+', '-/--', '-*//', '-*/*', '-*/+', '-*/-', '-**/', '-***', '-**+', '-**-', '-*+/', '-*+*', '-*++', '-*+-', '-*-/', '-*-*', '-*-+', '-*--', '-+//', '-+/*', '-+/+', '-+/-', '-+*/', '-+**', '-+*+', '-+*-', '-++/', '-++*', '-+++', '-++-', '-+-/', '-+-*', '-+-+', '-+--', '--//', '--/*', '--/+', '--/-', '--*/', '--**', '--*+', '--*-', '--+/', '--+*', '--++', '--+-', '---/', '---*', '---+', '----']
['///', '//*', '//+', '//-', '/*/', '/**', '/*+', '/*-', '/+/', '/+*', '/++', '/+-', '/-/', '/-*', '/-+', '/--', '*//', '*/*', '*/+', '*/-', '**/', '***', '**+', '**-', '*+/', '*+*', '*++', '*+-', '*-/', '*-*', '*-+', '*--', '+//', '+/*', '+/+', '+/-', '+*/', '+**', '+*+', '+*-', '++/', '++*', '+++', '++-', '+-/', '+-*', '+-+', '+--', '-//', '-/*', '-/+', '-/-', '-*/', '-**', '-*+', '-*-', '-+/', '-+*', '-++', '-+-', '--/', '--*', '--+', '---']
How can I do that?
Here's a generic solution for any value of 'repeat'.
def pr(l, repeat=1):
if repeat == 1:
return [[x] for x in l]
sub_prod = pr(l, repeat-1)
return [ [x] + y for x in l for y in sub_prod ]
o = ['/','*','+','-']
pr(o, 3)
Result:
[['/', '/', '/'],
['/', '/', '*'],
['/', '/', '+'],
['/', '/', '-'],
['/', '*', '/'],
['/', '*', '*'],
['/', '*', '+'],
['/', '*', '-'],
['/', '+', '/'],
['/', '+', '*'],
['/', '+', '+'],
...
If you want to turn each sublist to a string, use:
["".join(x) for x in pr(o, 3)]
Just wrap your command in a function
def f(l):
return [(x, y) for x in l for y in l]
or if you want the same generator behavior as itertools has:
def f(l):
return ((x, y) for x in l for y in l)
or a more verbose generator syntax:
def f(l):
for x in l:
for y in l:
yield x, y
Related
Why am I getting a garbage tokenizer?
from tokenizers import Tokenizer, models, normalizers, pre_tokenizers, decoders, trainers tokenizer = Tokenizer(models.Unigram()) tokenizer.normalizer = normalizers.NFKC() tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel() tokenizer.decoder = decoders.ByteLevel() trainer = trainers.UnigramTrainer( vocab_size=30000, initial_alphabet=pre_tokenizers.ByteLevel.alphabet(), special_tokens=["<PAD>", "<BOS>", "<EOS>", '<s>', '</s>', '<unk>', '<mask>'], min_frequency = 2 ) def batch_iterator(batch_size=10, size=5000): for i in range(100): query = f"select note_text from db.note where id > {i * size} limit 50;" df = pd.read_sql(sql=query, con=db) for x in range(0, size, batch_size): yield list(df['note_text'].loc[0:5000])[x:x + batch_size] tokenizer.train_from_iterator(batch_iterator(), trainer=trainer, length=100*5000) A single note may look something like this: !~!~!~!~!~!~!~!~!~!~!~!~!~!~Discussing settlement with Amy.!~!~ The output looks as follows: out = tokenizer.encode('There should be an inspection come Monday 1/2/2022!') ['ĠThe', 'r', 'e', 'Ġsh', 'ould', 'Ġbe', 'Ġan', 'Ġinspect', 'ion', 'Ġ', 'com', 'e', 'Ġ', 'M', 'ond', 'a', 'y', 'Ġ', '1', '/', '2', '/', '20', '2', '2', '!']
Python only encryption/obfuscation
I'm looking for simple password-based obfuscation/security of strings. I've pretty much gone over each example of > Simple way to encode a string according to a password? And none of them work with my python 3.7. I got the error with ord() so I updated the code, but even after, its still broken. For examle: from itertools import cycle def encode_zip_cycle(key, clear): enc = [chr((ord(clear_char) + ord(key_char)) % 256) for clear_char, key_char in zip(clear, cycle(key))] return base64.urlsafe_b64encode("".join(enc).encode()) def decode_zip_cycle(key, enc): enc = base64.urlsafe_b64decode(enc) dec = [chr((256 + enc_char - ord(key_char)) % 256) for enc_char, key_char in zip(enc, cycle(key))] print(dec) return "".join(dec) text = "ATTACKATONCEfor Live 2154125-21-512^!££613-123!" s = "1235348udgfjff" print("Text : " + text) print("Shift : " + str(s)) print("Cipher: ", encode_zip_cycle(s, text)) # , type(encode(s, text))) print("Original text: ", decode_zip_cycle(s, encode_zip_cycle(s, text))) Gives me Text : ATTACKATONCEfor Live 2154125-21-512^!££613-123! Shift : 1235348udgfjff Cipher: b'csKGwod2dn95w4nCs8K1wqnCr8OMw5XCo1J_wp7CqcKZWMKVwoTCmcKXwp_CmsKXY2dgZ2RhbcKmwpbDhcKHDQnCnGJlYGZlZ1k=' ['A', '\x90', 'S', '\x8d', 'T', 'B', '>', '\n', '\x15', '\\', '#', 'X', 'M', '\\', '\x84', '\x90', 'v', '\x8d', '|', '\x8f', 'T', 'N', '1', '[', '=', 'è', '\x19', '\\', 'm', '\x90', 'v', '\x8d', 'f', '$', '\x8a', ' ', '^', '\x1d', '\\', '/', '\\', '1', '\x91', 'm', '\x8f', 'e', '\x8f', 'c', '+', 'ò', 'ü', '\x00', 'þ', '÷', '\x07', '\\', 'u', '\x90', 'c', '\x8e', 'R', '\x8e', 'O', '\x98', '¥', '[', '6', 'ø', 'ÿ', 'ú', '5', '3', '4', '$'] Original text: ASTB> \#XM\v|TN1[=è\mvf$ ^\/\1mec+òü þ÷\ucRO¥[6øÿú534$
In encode_zip_cycle you encode the "encrypted" string into utf-8 before doing the second encoding into base64. Yet, you don't revert this operation later in decode_zip_cycle. This is the correct decode_zip_cycle function: def decode_zip_cycle(key, enc): enc = base64.urlsafe_b64decode(enc).decode() dec = [chr((256 + ord(enc_char) - ord(key_char)) % 256) for enc_char, key_char in zip(enc, cycle(key))] print(dec) return "".join(dec)
Making QComboBox widgets distinguishable in a for loop using PyQt5
I'm having trouble with an application that creates a grid of QComboBox widgets (see picture below). I use a for loop to create the elements of the grid. QComboBox grid layout I would like to be able to treat each QComboBox separately. Here is the code without attempts to do so: grid = QGridLayout() combos = [ '1', '1', '1', '', '1', '1', '1', '1', '', '1', '1', '1', '1', '', '1', '1', '1', '1', '', '1'] positions = [(i,j) for i in range(5) for j in range(5)] for position, dcombo in zip(positions, combos): if dcombo == '': continue combo = QComboBox() for x in range(0, 30): combo.addItem(QIcon("/icons/%d.PNG" % x),"") combo.setFixedSize(120,100) combo.setIconSize(QSize(100,100)) grid.addWidget(combo, *position) comboList['combo{0}'.format(position)] = position Here is my attempt, and the point at which I am currently stuck: grid = QGridLayout() combos = [ '1', '1', '1', '', '1', '1', '1', '1', '', '1', '1', '1', '1', '', '1', '1', '1', '1', '', '1'] comboList = {} positions = [(i,j) for i in range(5) for j in range(5)] for position, drawcombo in zip(positions, combos): if drawcombo == '': continue combo = QComboBox() for x in range(0, 30): # combo.addItem(QIcon("absolver deck reviewer/icons/%d.PNG" % x),"") combo.setFixedSize(120,100) combo.setIconSize(QSize(100,100)) grid.addWidget(combo, *position) comboList['combo{0}'.format(position)] = position combo.currentIndexChanged.connect(lambda: self.logChange(comboList['combo{0}'.format(position)])) def logChange(self, currentCombo): sender = self.sender() print(str(currentCombo) + ' was changed') The print() method only ever returns the last position in the list (in this case, the (3, 4) tuple.
As the position variable changes this is overwritten by it only prints the last one, if you want it not to be overwritten you must pass it as an argument to the lambda function, besides. And for this you must also pass as an argument the variable that sends the signal, in your case use the following: combo.currentIndexChanged.connect( lambda ix, p=position: self.logChange(comboList['combo{0}'.format(p)]))
How to change value in list for calendar?
The goal of my function is to book a slot in that calendar corresponding to the parameters day and time - booking is done by changing the value in the calendar to “X” (to indicate that day/time is booked). Here is the code I have so far: def find_index(val, seq): for index in range(len(seq)): place = seq[index] if place == val: return index else: return int("-1") def init_nested_list(size_outer, size_inner): cal = [] for outer_index in range(size_outer): nested_list = [] for inner_index in range(size_inner): nested_list.append("-") cal.append(nested_list) return cal def book_slot(cal,days_labels, times_labels, day, time): pos1 = find_index(day, days_labels) desired_day = cal[pos1] pos2 = find_index(time, times_labels) desired_time = desired_day[pos2] if desired_day == "X": print("We are sorry - that time is not available. Please try again.") else: print("Appointment is booked.") days_labels = ["Wednesday","Thursday","Friday"] times_labels = ["9","10","11"] cal = init_nested_list(len(days_labels), len(times_labels)) print("before" , cal) book_slot(cal, days_labels, times_labels, "Friday", "10") print("after" , cal) This is the output I'm getting: before [['-', '-', '-'], ['-', '-', '-'], ['-', '-', '-']] Appointment is booked. after [['-', '-', '-'], ['-', '-', '-'], ['-', '-', '-']] This is the output I should be getting: before [['-', '-', '-'], ['-', '-', '-'], ['-', '-', '-']] Appointment is booked. after [['-', '-', '-'], ['-', '-', '-'], ['-', 'X', '-']] We are sorry - that time is not available. Please try again. As you can see, the correct output books the second element in the nested list(Friday at 10), but for my code it is not. I know desired_day is the day in cal that the user wants to book, but I'm just unsure of how to properly obtain that, and assign it to the string "X" when it is booked by the user. I also know that there is a coding error in the book_slot function that is causing this, but once again, I'm just unsure what it is...help please?
Nowhere in book_slot do you assign X to anything. I should think you could use pos1 and pos2 to identify the slot to assign to.
This will work. See the Change # notes in the code def find_index(val, seq): return seq.index(val) #Change 1 def init_nested_list(size_outer, size_inner): cal = [] for outer_index in range(size_outer): nested_list = [] for inner_index in range(size_inner): nested_list.append("-") cal.append(nested_list) return cal def book_slot(cal, days_labels, times_labels, day, time): pos1 = find_index(day, days_labels) desired_day = cal[pos1] pos2 = find_index(time, times_labels) if desired_day[pos2] is "X": # Change 2 print("We are sorry - that time is not available. Please try again.") else: print("Appointment is booked.") desired_day[pos2] = "X" # Change 3 days_labels = ["Wednesday", "Thursday", "Friday"] times_labels = ["9", "10", "11"] cal = init_nested_list(len(days_labels), len(times_labels)) print("before", cal) book_slot(cal, days_labels, times_labels, "Friday", "10") book_slot(cal, days_labels, times_labels, "Friday", "10") # Change 4: Need to do a booking twice to make it fail :-) print("after", cal) will output: ('before', [['-', '-', '-'], ['-', '-', '-'], ['-', '-', '-']]) Appointment is booked. ('after', [['-', '-', '-'], ['-', '-', '-'], ['-', 'X', '-']]) We are sorry - that time is not available. Please try again.
How can I split a string of a mathematical expressions in python?
I made a program which convert infix to postfix in python. The problem is when I introduce the arguments. If i introduce something like this: (this will be a string) ( ( 73 + ( ( 34 - 72 ) / ( 33 - 3 ) ) ) + ( 56 + ( 95 - 28 ) ) ) it will split it with .split() and the program will work correctly. But I want the user to be able to introduce something like this: ((73 + ( (34- 72 ) / ( 33 -3) )) + (56 +(95 - 28) ) ) As you can see I want that the blank spaces can be trivial but the program continue splitting the string by parentheses, integers (not digits) and operands. I try to solve it with a for but I don't know how to catch the whole number (73 , 34 ,72) instead one digit by digit (7, 3 , 3 , 4 , 7 , 2) To sum up, what I want is split a string like ((81 * 6) /42+ (3-1)) into: [(, (, 81, *, 6, ), /, 42, +, (, 3, -, 1, ), )]
Tree with ast You could use ast to get a tree of the expression : import ast source = '((81 * 6) /42+ (3-1))' node = ast.parse(source) def show_children(node, level=0): if isinstance(node, ast.Num): print(' ' * level + str(node.n)) else: print(' ' * level + str(node)) for child in ast.iter_child_nodes(node): show_children(child, level+1) show_children(node) It outputs : <_ast.Module object at 0x7f56abbc5490> <_ast.Expr object at 0x7f56abbc5350> <_ast.BinOp object at 0x7f56abbc5450> <_ast.BinOp object at 0x7f56abbc5390> <_ast.BinOp object at 0x7f56abb57cd0> 81 <_ast.Mult object at 0x7f56abbd0dd0> 6 <_ast.Div object at 0x7f56abbd0e50> 42 <_ast.Add object at 0x7f56abbd0cd0> <_ast.BinOp object at 0x7f56abb57dd0> 3 <_ast.Sub object at 0x7f56abbd0d50> 1 As #user2357112 wrote in the comments : ast.parse interprets Python syntax, not mathematical expressions. (1+2)(3+4) would be parsed as a function call and list comprehensions would be accepted even though they probably shouldn't be considered a valid mathematical expression. List with a regex If you want a flat structure, a regex could work : import re number_or_symbol = re.compile('(\d+|[^ 0-9])') print(re.findall(number_or_symbol, source)) # ['(', '(', '81', '*', '6', ')', '/', '42', '+', '(', '3', '-', '1', ')', ')'] It looks for either : multiple digits or any character which isn't a digit or a space Once you have a list of elements, you could check if the syntax is correct, for example with a stack to check if parentheses are matching, or if every element is a known one.
You need to implement a very simple tokenizer for your input. You have the following types of tokens: ( ) + - * / \d+ You can find them in your input string separated by all sorts of white space. So a first step is to process the string from start to finish, and extract these tokens, and then do your parsing on the tokens, rather than on the string itself. A nifty way to do this is to use the following regular expression: '\s*([()+*/-]|\d+)'. You can then: import re the_input='(3+(2*5))' tokens = [] tokenizer = re.compile(r'\s*([()+*/-]|\d+)') current_pos = 0 while current_pos < len(the_input): match = tokenizer.match(the_input, current_pos) if match is None: raise Error('Syntax error') tokens.append(match.group(1)) current_pos = match.end() print(tokens) This will print ['(', '3', '+', '(', '2', '*', '5', ')', ')'] You could also use re.findall or re.finditer, but then you'd be skipping non-matches, which are syntax errors in this case.
If you don't want to use re module, you can try this: s="((81 * 6) /42+ (3-1))" r=[""] for i in s.replace(" ",""): if i.isdigit() and r[-1].isdigit(): r[-1]=r[-1]+i else: r.append(i) print(r[1:]) Output: ['(', '(', '81', '*', '6', ')', '/', '42', '+', '(', '3', '-', '1', ')', ')']
It actual would be pretty trivial to hand-roll a simple expression tokenizer. And I'd think you'd learn more that way as well. So for the sake of education and learning, Here is a trivial expression tokenizer implementation which can be extended. It works based upon the "maximal-much" rule. This means it acts "greedy", trying to consume as many characters as it can to construct each token. Without further ado, here is the tokenizer: class ExpressionTokenizer: def __init__(self, expression, operators): self.buffer = expression self.pos = 0 self.operators = operators def _next_token(self): atom = self._get_atom() while atom and atom.isspace(): self._skip_whitespace() atom = self._get_atom() if atom is None: return None elif atom.isdigit(): return self._tokenize_number() elif atom in self.operators: return self._tokenize_operator() else: raise SyntaxError() def _skip_whitespace(self): while self._get_atom(): if self._get_atom().isspace(): self.pos += 1 else: break def _tokenize_number(self): endpos = self.pos + 1 while self._get_atom(endpos) and self._get_atom(endpos).isdigit(): endpos += 1 number = self.buffer[self.pos:endpos] self.pos = endpos return number def _tokenize_operator(self): operator = self.buffer[self.pos] self.pos += 1 return operator def _get_atom(self, pos=None): pos = pos or self.pos try: return self.buffer[pos] except IndexError: return None def tokenize(self): while True: token = self._next_token() if token is None: break else: yield token Here is a demo the usage: tokenizer = ExpressionTokenizer('((81 * 6) /42+ (3-1))', {'+', '-', '*', '/', '(', ')'}) for token in tokenizer.tokenize(): print(token) Which produces the output: ( ( 81 * 6 ) / 42 + ( 3 - 1 ) )
Quick regex answer: re.findall(r"\d+|[()+\-*\/]", str_in) Demonstration: >>> import re >>> str_in = "((81 * 6) /42+ (3-1))" >>> re.findall(r"\d+|[()+\-*\/]", str_in) ['(', '(', '81', '*', '6', ')', '/', '42', '+', '(', '3', '-', '1', ')', ')'] For the nested parentheses part, you can use a stack to keep track of the level.
This does not provide quite the result you want but might be of interest to others who view this question. It makes use of the pyparsing library. # Stolen from http://pyparsing.wikispaces.com/file/view/simpleArith.py/30268305/simpleArith.py # Copyright 2006, by Paul McGuire # ... and slightly altered from pyparsing import * integer = Word(nums).setParseAction(lambda t:int(t[0])) variable = Word(alphas,exact=1) operand = integer | variable expop = Literal('^') signop = oneOf('+ -') multop = oneOf('* /') plusop = oneOf('+ -') factop = Literal('!') expr = operatorPrecedence( operand, [("!", 1, opAssoc.LEFT), ("^", 2, opAssoc.RIGHT), (signop, 1, opAssoc.RIGHT), (multop, 2, opAssoc.LEFT), (plusop, 2, opAssoc.LEFT),] ) print (expr.parseString('((81 * 6) /42+ (3-1))')) Output: [[[[81, '*', 6], '/', 42], '+', [3, '-', 1]]]
Using grako: start = expr $; expr = calc | value; calc = value operator value; value = integer | "(" #:expr ")" ; operator = "+" | "-" | "*" | "/"; integer = /\d+/; grako transpiles to python. For this example, the return value looks like this: ['73', '+', ['34', '-', '72', '/', ['33', '-', '3']], '+', ['56', '+', ['95', '-', '28']]] Normally you'd use the generated semantics class as a template for further processing.
To provide a more verbose regex approach that you could easily extend: import re solution = [] pattern = re.compile('([\d\.]+)') s = '((73 + ( (34- 72 ) / ( 33 -3) )) + (56 +(95 - 28) ) )' for token in re.split(pattern, s): token = token.strip() if re.match(pattern, token): solution.append(float(token)) continue for character in re.sub(' ', '', token): solution.append(character) Which will give you the result: solution = ['(', '(', 73, '+', '(', '(', 34, '-', 72, ')', '/', '(', 33, '-', 3, ')', ')', ')', '+', '(', 56, '+', '(', 95, '-', 28, ')', ')', ')']
Similar to #McGrady's answer, you can do this with a basic queue implementation. As a very basic implementation, here's what your Queue class can look like: class Queue: EMPTY_QUEUE_ERR_MSG = "Cannot do this operation on an empty queue." def __init__(self): self._items = [] def __len__(self) -> int: return len(self._items) def is_empty(self) -> bool: return len(self) == 0 def enqueue(self, item): self._items.append(item) def dequeue(self): try: return self._items.pop(0) except IndexError: raise RuntimeError(Queue.EMPTY_QUEUE_ERR_MSG) def peek(self): try: return self._items[0] except IndexError: raise RuntimeError(Queue.EMPTY_QUEUE_ERR_MSG) Using this simple class, you can implement your parse function as: def tokenize_with_queue(exp: str) -> List: queue = Queue() cum_digit = "" for c in exp.replace(" ", ""): if c in ["(", ")", "+", "-", "/", "*"]: if cum_digit != "": queue.enqueue(cum_digit) cum_digit = "" queue.enqueue(c) elif c.isdigit(): cum_digit += c else: raise ValueError if cum_digit != "": #one last sweep in case there are any digits waiting queue.enqueue(cum_digit) return [queue.dequeue() for i in range(len(queue))] Testing it like below: exp = "((73 + ( (34- 72 ) / ( 33 -3) )) + (56 +(95 - 28) ) )" print(tokenize_with_queue(exp)") would give you the token list as: ['(', '(', '73', '+', '(', '(', '34', '-', '72', ')', '/', '(', '33', '-', '3', ')', ')', ')', '+', '(', '56', '+', '(', '95', '-', '28', ')', ')', ')']