I'm having issue with this function question:
x = [[0,0,0,0,0],[0,0,0,0,1],[0,1,0,0,0]]
Function: Book(seat) #assuming the seat is A5
The function assumes the seat is valid in the format A, B and C. the function needs to transform the letter part of seat to an integer A = 0, B = 1 and C = 2. The string digit also needs to be changed to "1" → 0, "2" → 1, "3" → 2, "4" → 3 and "5" → 4. These can be used to check if the chair in the x list-of-lists is already booked 1 or not 0. If it is not booked, then it should be changed to booked and the function should return True else it should return False.
My solution is
a = {"A":[0,0,0,0,0], "B":[0,0,0,0,1], "C":[0,1,0,0,],}
rowIndex = ["A","B","C"]
columnIndex = [1,2,3,4,5]
def book(seat):
row = seat[0]
column = seat[1]
while row in rowIndex and column in columnIndex:
if x[row][column-1] == 0:
return True
else: return False
It output False (seat already booked) in respective of the seat I book. I think there is an issue with my code but can't seems to figure it out.
There are a number of problems with your code for the function:
There is no x variable defined — you called it a in the
a = {"A":[0,0,0,0,0], "B":[0,0,0,0,1], "C":[0,1,0,0,],}
After the
row = seat[0]
column = seat[1]
you then test the values in the following:
while row in rowIndex and column in columnIndex:
which will prevent any of the rest of the code from executing unless it's True.
What you need inside of the while to iterate through all the possibilities would require two for loops, one nested inside the other. However…
You don't need to loop at all as illustrated below.
BOOKED = 1
x = [[0,0,0,0,0], [0,0,0,0,1], [0,1,0,0,0]]
letter_to_index = {"A": 0, "B": 1, "C": 2}
digit_to_index = {"1": 0, "2": 1, "3": 2, "4": 3, "5": 4}
def book(seat):
# Convert each seat character to integer.
row = letter_to_index[seat[0]]
col = digit_to_index[seat[1]]
if x[row][col] == BOOKED:
return False
else:
# Book the seat and return True
x[row][col] = BOOKED
return True
if __name__ == '__main__':
print(book('A5')) # -> True
# Try doing it again.
print(book('A5')) # -> False
Here is a simpler implementation of your code. You don't need to use loops. You have a dictionary. You can lookup the dictionary in a much simpler way.
a = {"A":[0,0,0,0,0], "B":[0,0,0,0,1], "C":[0,1,0,0,],}
def book(seat):
r,c = seat #this allows A to be r and 5 to be C
#check if value of r in keys of a
#also check if seat # is within length of seats for key
if r in a and int(c) <= len(a[r]):
#if valid request, then check if seat already booked
#if not, set seat to booked by setting value to 1
#return True
#if already booked, return False
if a[r][int(c)-1] == 0:
a[r][int(c)-1] = 1
return True
else:
return False
# if not a value request, send appropriate message
else:
return 'invalid request'
print ('A5', book('A5'))
print ('C2', book('C2'))
print ('A7', book('A7'))
print (a)
Output of this will be:
A5 True
C2 False
A7 invalid request
{'A': [0, 0, 0, 0, 1], 'B': [0, 0, 0, 0, 1], 'C': [0, 1, 0, 0]}
Related
This question already has answers here:
What is the purpose of the return statement? How is it different from printing?
(15 answers)
Closed 6 months ago.
I have this code written down which returns a bunch of ratios
each line works fine when executed alone
however I am having trouble with putting the code inside a function
which should return the values as a dictionary
example of the output: { " less than high school" :0.2 , "high school" :0.39 ...}
def education ():
total = sum (df["CHILDNM"])
edult12 = df[['EDUC1','CHILDNM']].where(df['EDUC1'] == 1, 0)
nlt12=sum( edult12["CHILDNM"])
edu12 = df[['EDUC1','CHILDNM']].where(df['EDUC1'] == 2, 0)
n12= sum( edu12["CHILDNM"])
edumt12 = df[['EDUC1','CHILDNM']].where(df['EDUC1'] == 3, 0)
nmt12=sum( edumt12["CHILDNM"])
educollege = df[['EDUC1','CHILDNM']].where(df['EDUC1'] == 4, 0)
ncollege = sum( educollege["CHILDNM"])
lt12ratio = nlt12/total
edu12ratio = n12 / total
edumt12ratio = nmt12 / total
educollegeratio = ncollege/total
values = print (lt12ratio, edu12ratio,edumt12ratio, educollegeratio )
print (values)
Your code is missing the return statement!
Add something like
return { "less than high school" :0.2 , "high school" :0.39 }
(replace numbers with your actual calculated values)
It seems you do not have any "return" statement. So this means the function will not give back any value.
def education ():
somedict = {'url': 'https://stackoverflow.com/'}
return somedict
save_return_value = education()
The "save_return_value" could then include the return value
Well, you assign to values the result of calling print which is None.
Then you simply print out the content of values which should print None.
Finally, you don't have any return statement and so your education function will always return None
You need to assign the variables (lt12ratio, edu12ratio,edumt12ratio, educollegeratio) to values, not print.
If you want to print them, then you use print(value). Finally, you need to return the values variable.
This is how your function should be:
def education():
total = sum (df["CHILDNM"])
edult12 = df[['EDUC1','CHILDNM']].where(df['EDUC1'] == 1, 0)
nlt12=sum( edult12["CHILDNM"])
edu12 = df[['EDUC1','CHILDNM']].where(df['EDUC1'] == 2, 0)
n12= sum( edu12["CHILDNM"])
edumt12 = df[['EDUC1','CHILDNM']].where(df['EDUC1'] == 3, 0)
nmt12=sum( edumt12["CHILDNM"])
educollege = df[['EDUC1','CHILDNM']].where(df['EDUC1'] == 4, 0)
ncollege = sum( educollege["CHILDNM"])
lt12ratio = nlt12/total
edu12ratio = n12 / total
edumt12ratio = nmt12 / total
educollegeratio = ncollege/total
values = (lt12ratio, edu12ratio,edumt12ratio, educollegeratio)
print(values)
return values
I want to optimise my model that finds the highest possible overlap (assignment) of items (probes) against a sequence (sequence). I have the starting and end positions of all items and can thus build up my model as follows:
import pyomo
import pyomo.environ as pe
import pyomo.opt as po
sequence = [0, 1, 2, 3, 4, 5]
probes = ["a", "b", "c"]
probe_starts = {"a": 0, "b": 2, "c": 3}
probe_ends = {"a": 2, "b": 4, "c": 5}
# Model definition
model = pe.ConcreteModel()
model.sequence = pe.Set(initialize=sequence)
model.probes = pe.Set(initialize=probes)
model.starts = pe.Param(model.probes, initialize=probe_starts)
model.ends = pe.Param(model.probes, initialize=probe_ends)
model.assignment = pe.Var(model.sequence, model.probes, domain=pe.Binary)
# Objective
expr = sum([model.assignment[j, i] for j in model.sequence for i in model.probes])
model.objective = pe.Objective(expr=expr, sense=pe.maximize)
I now have the following three constraints:
Items can only bind one at a time (no overlapping items)
Because items have a start and end position they are limited in only getting assigned after their respective starts and ends
If items are assigned, they have to bind in their entirety spanning from their start to their end
# One probe per sequence position
model.one_probe_bound = pe.ConstraintList()
for s in model.sequence:
model.one_probe_bound.add(sum(model.assignment[s, p] for p in model.probes) <= 1)
# No assignment before/after start/end
model.define_length = pe.ConstraintList()
for s in model.sequence:
for p in model.probes:
if s < model.starts[p]:
model.define_length.add(model.assignment[s, p] == 0)
if s > model.ends[p]:
model.define_length.add(model.assignment[s, p] == 0)
Both of the constraints above work without issue but I can't find a way to input the logical or from my third condition. I tried to use the disjunction as described in this stackoverflow answer:
# Only allow full assignment or none
def disjunct_rule(b, p, i):
m = b.model()
expr = sum(m.assignment[s, p] for s in m.sequence)
if i:
return expr == m.ends[p] - m.starts[p]
else:
return expr == 0
def disjunction_rule(m, p):
return [m.disjunct[p, i] for i in [True, False]]
def xfrm(m):
pe.TransformationFactory("gdp.bigm").apply_to(m)
model.disjunct = pyomo.gdp.Disjunct(model.probes, [True, False], rule=disjunct_rule)
model.disjunction = pyomo.gdp.Disjunction(model.probes, rule=disjunction_rule)
model.xfrm = pe.BuildAction(rule=xfrm)
Looking at the matrix representation of model.assignment with sequence along the columns and probes along the rows, I get the following:
array([[1, 1, 1, 0, 0, 0],
[0, 0, 0, 1, 1, 0],
[0, 0, 0, 0, 0, 1]])
As can be seen above, I get assignments that aren't spanning the entire length of the item (e.g. c / 3rd item is only assigned at the last position whereas it should have to bind at the two previous ones too. The only valid solution I can see in this toy example is the following:
array([[1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 1]])
Where items a and c are selected in their entirety and b isn't selected at all. This way we have all constraints matched. The solver I used was glpk. Thanks in advance for any suggestions.
Here is a cut at this....
The other way I mentioned would be to introduce another binary variable for each point in sequence and control that (somehow) via the assignment of probes. The method below should be much more efficient than that.
Try this out with a larger dataset... The current only has 1 feasible solution. Also, I assumed that better solutions would use fewer probes, so I re-wrote the objective to that.
The basis of this solution is the constraint that you must connect to the start once (constraint 1) and the end (constraint 2) once. And any intermediate connections must be consistent (constraint 3).
I used some sub-setting in a few spots where needed.
Code:
# model to make contiguous connections across a sequence
# using as few connections (probes) as possible
import pyomo
import pyomo.environ as pe
sequence = [0, 1, 2, 3, 4, 5]
probes = ["a", "b", "c"]
probe_starts = {"a": 0, "b": 2, "c": 3}
probe_ends = {"a": 2, "b": 4, "c": 5}
# Model definition
model = pe.ConcreteModel()
model.sequence = pe.Set(initialize=sequence)
model.probes = pe.Set(initialize=probes)
model.starts = pe.Param(model.probes, initialize=probe_starts)
model.ends = pe.Param(model.probes, initialize=probe_ends)
model.assign = pe.Var(model.probes, domain=pe.Binary) # 1 if probe p is used...
# Objective
obj = sum(model.assign[p] for p in model.probes) # use as few as possible...?
model.objective = pe.Objective(expr=obj, sense=pe.minimize)
# Constraints
# must connect start once
model.C1 = pe.Constraint(expr=sum(model.assign[p] for p in model.probes
if model.starts[p] == sequence[0]) == 1)
# must connect end once
model.C2 = pe.Constraint(expr=sum(model.assign[p] for p in model.probes
if model.ends[p] == sequence[-1]) == 1)
# must connect any intermediate connections...
# if probe p1 is selected, must select an eligible p2 follow on
def connect(model, p1):
# create subset on the fly of legal follow-on connections
# assumption here that sequence is a sequential list of ints by using the "+1"
p2s = [p for p in model.probes if model.starts[p] == model.ends[p1] + 1]
if not p2s:
return pe.Constraint.Skip
return sum(model.assign[p2] for p2 in p2s) == model.assign[p1]
non_completing_probes = [p for p in model.probes if model.ends[p] != sequence[-1]]
model.C3 = pe.Constraint(non_completing_probes, rule=connect)
solver = pe.SolverFactory('glpk')
result = solver.solve(model)
print(result)
model.display()
Yields:
Problem:
- Name: unknown
Lower bound: 2.0
Upper bound: 2.0
Number of objectives: 1
Number of constraints: 4
Number of variables: 4
Number of nonzeros: 5
Sense: minimize
Solver:
- Status: ok
Termination condition: optimal
Statistics:
Branch and bound:
Number of bounded subproblems: 0
Number of created subproblems: 0
Error rc: 0
Time: 0.006771087646484375
Solution:
- number of solutions: 0
number of solutions displayed: 0
Model unknown
Variables:
assign : Size=3, Index=probes
Key : Lower : Value : Upper : Fixed : Stale : Domain
a : 0 : 1.0 : 1 : False : False : Binary
b : 0 : 0.0 : 1 : False : False : Binary
c : 0 : 1.0 : 1 : False : False : Binary
Objectives:
objective : Size=1, Index=None, Active=True
Key : Active : Value
None : True : 2.0
Constraints:
C1 : Size=1
Key : Lower : Body : Upper
None : 1.0 : 1.0 : 1.0
C2 : Size=1
Key : Lower : Body : Upper
None : 1.0 : 1.0 : 1.0
C3 : Size=1
Key : Lower : Body : Upper
a : 0.0 : 0.0 : 0.0
There is a hotel e.g. of size 7x5. I need to create a function where
a number is given as parameter for finding the amount of consecutive empty rooms
returns the number of floor and room number in that.
(depicted below: 0 is empty room and 1 is full)
e.g.:
if the parameter is 1, output will be
"floor no: 5, start from room no: 1"
if the parameter is 2, output will be
"floor no: 5, start from room no: 3"
if the parameter is 3, output will be
"floor no: 5, start from room no: 3"
if the parameter is 4, output will be
"floor no: 4, start from room no: 4"
if the parameter is 5, output will be
"floor no: 2, start from room no: 1"
if the parameter is 6 (or 7), output will be
"floor no: 1, start from room no: 1"
if the parameter is > 7, output will be
"not possible to find in one floor"
preferably without using itertools.grupby.
My try:
def adjacent_rooms (amount):
nested_list_temp = [[0]*7]*5
nested_list = [list(i) for i in nested_list_temp]
nested_list [1][5] = 1
nested_list [2][3] = 1
nested_list [2][4] = 1
nested_list [3][2] = 1
nested_list [4][1] = 1
nested_list [4][5] = 1
# [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 1, 0, 0], [0, 0, 1, 0, 0, 0, 0], [0, 1, 0, 0, 0, 1, 0]]
try:
for i in range(len(nested_list), 0, -1):
for j in range(len(nested_list[0])):
if nested_list[i-1][j] == 0:
count += 1
if count == amount:
return (i, j-amount+2)
else:
count = 0
except:
return "not possible to find in one floor"
Any effective hints or suggestions will be highly appreciated.
Use narrow try-except conditions. The first problem is your all-encompassing
error catching, which hides a wide variety of failures -- including outright
code bugs, such as your failure to initialize count. So the first thing I did
was to delete the try-except. You don't really need it here. And even if you
did, you want to declare one or more specific exceptions in the except clause
rather than leaving it wide open.
Work within Python's list indexing as long as possible. It seems that you
want to return human-oriented floor/room numbers (starting at 1) rather
than computer-oriented numbers (starting at 0). That's fine. But defer the
computer-to-human conversion as long as possible. Within the guts of your
algorithmic code, work with Python's indexing scheme. In your case, your
code straddles both, sometimes using 1-based indexing, sometimes 0-based.
That's confusing.
You are resetting count too often. It should be set whenever the room
is full. But you are resetting it whenever count does not equal amount.
As a result, count is almost always being reset to zero.
You are also resetting count too infrequently. It must be reset at the
start of each new floor.
If we make those changes, we get this:
def adjacent_rooms(nested_list, amount):
for i in range(len(nested_list), 0, -1):
count = 0
for j in range(len(nested_list[0])):
if nested_list[i-1][j] == 0:
count += 1
if count == amount:
return (i, j-amount+2)
else:
count = 0
Python lists are directly iterable. As a result, you almost never
need to mess around with list indexes and range() to process list data.
Just iterate directly to access the values. And for those cases where
you need both the value and the index, use enumerate().
Use more declarative variable names. Names like hotel, floor, and
room help the reader understand your code.
Return data, not textual messages. If a function returns a tuple of
integers upon success, what should it do upon non-serious failure? It depends
on the context, but you can either raise an exception or return some variant of
None. In your case, I would probably opt for a parallel tuple: (None, None). This allows the caller to interact with the function in a fairly
natural way and then simply check either value for None. But returning a
textual message is quite unhelpful for callers: the returned data bundle has a
different outer structure (string vs tuple), and it has a different inner data
type (string vs int).
Don't depend on global variables. Pass the hotel data into
the function, as a proper argument.
If we make those changes, we get something like this:
def adjacent_rooms(hotel, wanted):
for fi, floor in enumerate(hotel):
n = 0
for ri, room in enumerate(floor):
if room == 0:
n += 1
if n == wanted:
return (fi + 1, ri - n + 2)
else:
n = 0
return (None, None)
I'm getting the desired output with slightly different indentation:
nested_list_temp = [[0]*7]*5
nested_list = [list(i) for i in nested_list_temp]
nested_list [1][5] = 1
nested_list [2][3] = 1
nested_list [2][4] = 1
nested_list [3][2] = 1
nested_list [4][1] = 1
nested_list [4][5] = 1
def adjacent_rooms(amount):
for i in range(len(nested_list), 0, -1):
count = 0
for j in range(len(nested_list[0])):
if nested_list[i-1][j] == 0:
count += 1
if count == amount:
return (i, j-amount+2)
else:
count = 0
return "not possible to find in one floor"
Another solution:
def adjacent_rooms(nested_list, amount):
to_search = "0" * amount
for floor in range(len(nested_list) - 1, -1, -1):
try:
idx = "".join(map(str, nested_list[floor])).index(to_search)
return "floor no: {}, start from room no: {}".format(
floor + 1, idx + 1
)
except ValueError:
continue
return "not possible to find in one floor"
nested_list = [[0 for _ in range(7)] for _ in range(5)]
nested_list[1][5] = 1
nested_list[2][3] = 1
nested_list[2][4] = 1
nested_list[3][2] = 1
nested_list[4][1] = 1
nested_list[4][5] = 1
for f in range(1, 10):
print("f={}, result: {}".format(f, adjacent_rooms(nested_list, f)))
Prints:
f=1, result: floor no: 5, start from room no: 1
f=2, result: floor no: 5, start from room no: 3
f=3, result: floor no: 5, start from room no: 3
f=4, result: floor no: 4, start from room no: 4
f=5, result: floor no: 2, start from room no: 1
f=6, result: floor no: 1, start from room no: 1
f=7, result: floor no: 1, start from room no: 1
f=8, result: not possible to find in one floor
f=9, result: not possible to find in one floor
I have problem with dict's method - copy(),
I make copy of the "tab_bin" - "HERE 02" in the code and then I modifi it - "HERE 01".
And when at the end of code it prints "tab_bin", it prints modyfied version of "tab_bin".
Also I know my code is a litte messy and names of variables look weird because they are in polish.
My code:
def setting(akcje,n,m):
def zmiana():
Zm_1 = []
Zm_2 = []
for x in range(akcje[1], akcje[3]+1):
Zm_1.append(x)
for y in range(akcje[0], akcje[2]+1):
Zm_2.append(y)
return Zm_1, Zm_2
def kreator_tab():
if bool(tab_bin) == False:
for x in range(1, n+1):
for y in range(m):
tab_bin.setdefault(x, []).append(0)
for key, value in tab_bin.items():
for x in Zm_2:
if key == x:
for y in Zm_1:
if value[y-1] == 0: value[y-1] = 1
else: value[y-1] = 0
def operacje_proste(akcje, liczba_operacji):
def kreator_zmian():
ZmX = []
ZmY = []
for x in range(1, akcje[1]+1):
ZmX.append(x)
for y in range(1, akcje[0]+1):
ZmY.append(y)
return ZmX, ZmY
def kreator_tab_prostych():
for keys, values in tab_operacyjna.items(): # <- HERE 01
for x in ZmY:
if keys == x:
for y in ZmX:
if values[y-1] == 0: values[y-1] = 1
else: values[y-1] = 0
return liczba_operacji+1
def d():
suma =0
for value in tab_operacyjna.values():
for x in value:
suma+=x
if suma == 0: return print(liczba_operacji)
else:
return operacje_proste([max(ZmY),max(ZmX)-1], liczba_operacji)
ZmX,ZmY= kreator_zmian()
liczba_operacji = kreator_tab_prostych()
d()
Zm_1, Zm_2 = zmiana()
kreator_tab()
tab_operacyjna = tab_bin.copy() # <- HERE 02
operacje_proste([max(Zm_2),max(Zm_1)], 0)
def wrapper():
n = 2
m = 3
q = 3
akcje = [1,2,2,2]
setting(akcje,n,m)
print(tab_bin)
wrapper()
[EDIT]:
Okey, to explain more clearly what I mean I will add print() to the code and show output.
Code:
tab_operacyjna = tab_bin.copy()
print(tab_bin, "- tab_bin ",tab_operacyjna,"- tab_operacyjna")
operacje_proste([max(Zm_2),max(Zm_1)], 0)
print(tab_bin, "- tab_bin ",tab_operacyjna,"- tab_operacyjna")
OutPut:
{1: [0, 1, 0], 2: [0, 1, 0]} - tab_bin {1: [0, 1, 0], 2: [0, 1, 0]} - tab_operacyjna
{1: [0, 0, 0], 2: [0, 0, 0]} - tab_bin {1: [0, 0, 0], 2: [0, 0, 0]} - tab_operacyjna
Even if in operacje_proste() I don't use tab_bin it changes just like tab_operacyja on which I make changes. I hope this edit will make it more clear.
As juanpa.arrivillaga wrote I thought that .copy works like .deepcopy(). For anyone who has problem like this one just check this.
I am working on a practice problem where we are to input a list into a function argument, that will represent a tic tac toe board, and return the outcome of the board. That is, X wins, O wins, Draw, or None (null string).
I have it solved, but I was wondering if there is a way I could manipulate my algorithm into a loop, as it was recommended to use a loop to compare each element of the main diagonal with all the
elements of its intersecting row and column, and then check the two diagonals. I'm new to python, so my solution might be a bit longer then it needs to be. How could a loop be implemented to check the outcome of the tic tac toe board?
def gameState (List):
xcounter=0
ocounter=0
if List[0][0]==List[0][1] and List[0][0]==List[0][2]:
return List[0][0]
elif List[0][0]==List[1][0] and List[0][0]==List[2][0]:
return List[0][0]
elif List[0][0]==List[1][1] and List[0][0]==List[2][2]:
return List[0][0]
elif List[1][1]==List[1][2] and List[1][1]==List[1][0] :
return List[1][1]
elif List[1][1]==List[0][1] and List[1][1]==List[2][1]:
return List[1][1]
elif List[1][1]==List[0][0] and List[1][1]==List[2][2]:
return List[1][1]
elif List[2][2]==List[2][0] and List[2][2]==List[2][1]:
return List[2][2]
elif List[2][2]==List[1][2] and List[2][2]==List[0][2]:
return List[2][2]
elif List[2][2]==List[1][1] and List[2][2]==List[0][0]:
return List[2][2]
for listt in List:
for elm in listt:
if elm=="X" or elm=="x":
xcounter+=1
elif elm=="O" or elm=="o":
ocounter+=1
if xcounter==5 or ocounter==5:
return "D"
else:
return ''
First up, there are only eight ways to win at TicTacToe. You have nine compare-and-return statements so one is superfluous. In fact, on further examination, you check 00, 11, 22 three times (cases 3, 6 and 9) and totally miss the 02, 11, 20 case.
In terms of checking with a loop, you can split out the row/column checks from the diagonals as follows:
# Check all three rows and columns.
for idx in range(3):
if List[0][idx] != ' ':
if List[0][idx] == List[1][idx] and List[0][idx] == List[2][idx]:
return List[0][idx]
if List[idx][0] != ' ':
if List[idx][0] == List[idx][1] and List[idx][0] == List[idx][2]:
return List[idx][0]
# Check two diagonals.
if List[1][1] != ' ':
if List[1][1] == List[0][0] and List[1][1] == List[2][2]:
return List[1][1]
if List[1][1] == List[0][2] and List[1][1] == List[2][0]:
return List[1][1]
# No winner yet.
return ' '
Note that this ensures a row of empty cells isn't immediately picked up as a win by nobody. You need to check only for wins by a "real" player. By that, I mean you don't want to detect three empty cells in the first row and return an indication based on that if the second row has an actual winner.
Of course, there are numerous ways to refactor such code to make it more easily read and understood. One way is to separate out the logic for checking a single line and then call that for each line:
# Detect a winning line. First cell must be filled in
# and other cells must be equal to first.
def isWinLine(brd, x1, y1, x2, y2, x3, y3):
if brd[x1][y1] == ' ': return False
return brd[x1][y1] == brd[x2][y2] and brd[x1][y1] == brd[x3][y3]
# Get winner of game by checking each possible line for a winner,
# return contents of one of the cells if so. Otherwise return
# empty value.
def getWinner(brd):
# Rows and columns first.
for idx in range(3):
if isWinLine(brd, idx, 0, idx, 1, idx, 2): return brd[idx][0]
if isWinLine(brd, 0, idx, 1, idx, 2, idx): return brd[0][idx]
# Then diagonals.
if isWinLine(brd, 0, 0, 1, 1, 2, 2): return brd[1][1]
if isWinLine(brd, 2, 0, 1, 1, 0, 2): return brd[1][1]
# No winner yet.
return ' '
Then you can just use:
winner = getWinner(List)
in your code and you'll either get back the winner or an empty indication if there isn't one.