How do you change a specific element of a set? - python

In this code, I am trying to compare the value of a set that has been looped each time to a value passed (in this case a) in a parameter. What's interesting though is that it shows when I use a for each loop that each element is an integer. How do I get a integer to integer comparison without a console error?
def remove(s,a,b):
c=set()
c=s
for element in c:
element=int(element)
if(element<a or element>b):
c.discard(element)
return c
def main():
remove({3, 17, -1, 4, 9, 2, 14}, 1, 10)
main()
Output:
if(element<=a or element>=b):
TypeError: '>=' not supported between instances of 'int' and 'set'

You reassign your local variable b:
def remove(s,a,b):
b=set() # now b is no longer the b you pass in, but an empty set
b=s # now it is the set s that you passed as an argument
# ...
if(... element > b): # so the comparison must fail: int > set ??
Short implementation using a set comprehension:
def remove(s, a, b):
return {x for x in s if a <= x <= b}
>>> remove({3, 17, -1, 4, 9, 2, 14}, 1, 10)
{9, 2, 3, 4}

if you want int to int compare then make b as list of s.
def remove(s,a,b):
b = list(s)
for element in s:
element=int(element)
if(element< a or element > b):
b.remove(element)
return b
def main():
remove({3, 17, -1, 4, 9, 2, 14}, 1, 10)
main()

Come on, why don't we make the code shorter?
Try this:
def remove(s, a, b):
return s.difference(filter(lambda x: not int(a) < int(x) < int(b), s))
def main():
new_set = remove({3, 17, -1, 4, 9, 2, 14}, 1, 10)
# {2, 3, 4, 9}
print(new_set)
main()

Related

Understanding closure scope in Python

This is example from Bratt Slatkin's book
def sort_priority(values, group):
def helper(x):
if x in group:
return (0, x)
return (1, x)
values.sort(key=helper)
Furthermore they gave these values
numbers = [8, 3, 1, 2, 5, 4, 7, 6]
group = {2, 3, 5, 7}
sort_priority(numbers, group)
print(numbers)
And we have
[2, 3, 5, 7, 1, 4, 6, 8]
I do not understand this example.Why do we have return two times and what does helper function actually do?
You read the function as:
def helper(x):
if x in group:
return (0, x)
else:
return (1, x)
Or, more concisely,
def helper(x):
return (x not in group, x)
The intuition behind this is that sort accepts a key callback which is called on each element. For each element, helper is invoked which returns a tuple (could be either (0, x) or (1, x) depending on whether x exists in the VIP list).
You should understand that tuples are sorted based on multiple predicates, meaning both items in the tuples are considered when deciding the order of elements. This would imply that elements for which group returns (0, x) will be ordered first compared to those returning (1, x) because 0 < 1.
After this, we have two groups, those with first element 0 and those with first element 1. All 0 group elements will come first, but the order of those elements depends on the second item in the tuples - x. And similar for 1 group elements.
For your input:
Group0: [2, 3, 5, 7]
Group1: [8, 1, 4, 6]
Ordering within Group0: [2, 3, 5, 7]
Ordering within Group1: [1, 4, 6, 8]
Overall ordering: [Group0, Group1]
Result: [2, 3, 5, 7, 1, 4, 6, 8]
Why do we have return two times?
This has nothing to do with closures or nested functions.
def helper(x):
if x in group:
return (0, x)
return (1, x)
Can be written as
def helper(x):
if x in group:
return (0, x)
else:
return (1, x)
Either way, the return value depends on what the if statement is evaluated to.
If it is True then (0, x) will be returned. If it is False then (1, x) will be returned.
Note that the first return statement is within the if block. In python whenever a function encounters a return statement, the execution is handed back to the caller
In your example, the two returns are just a shortcut way to avoid if else statements. When a particular value is in the group, (0,x) is returned and if the if condition is not satisfied, then (1,x) is returned.
It's a bit easier to understand the code when it's written without nested functions:
def helper(x):
global group
if x in group:
return 0, x
return 1, x
def sort_priority(values):
values.sort(key=helper)
numbers = [8, 3, 1, 2, 5, 4, 7, 6]
group = {2, 3, 5, 7}
sort_priority(numbers)
print(numbers)
Now it's easy to see that sort_priority() simply sorts the values by calling the helper function which creates an order by assigning a value to each x.
When the helper function is called with a value that's in group - it gets "lower" priority (zero) while if the value is not in group, it gets higher priority (one).
A closer look at helper indeed shows:
def helper(x):
global group
if x in group:
return 0, x # <-- we're in the `if` so `x` gets zero
return 1, x # <-- if we got here it means we didn't get into the `if` so `x` gets one
So by using the helper as a key function in the sorting, we'll get and ordered lists which puts the items that are in group first and only then the items that are not in group:
[2, 3, 5, 7, 1, 4, 6, 8]
^
The first item that is not in group
It is more obvious for me to use sorted(values) function instead of values.sort(), otherwise it is little ambiguous "what is returned?", "how actually helper is used?".
def sort_priority(values, group):
def helper(x):
if x in group:
return (0, x)
return (1, x)
sorted_values = sorted(values, key=helper)
return sorted_values
numbers = [8, 3, 1, 2, 5, 4, 7, 6]
group = {2, 3, 5, 7}
print('Sorted Numbers List: ', sort_priority(numbers, group))
Of course after sorted() is used, sorted list returned it explicitly.

take a number and sum its digits

I'm working through this Kata and although I've looked through the solutions none are quite similar enough to mine to answer my question.
Problem Text: The number 89 is the first integer with more than one digit that fulfills the property partially introduced in the title of this kata. What's the use of saying "Eureka"? Because this sum gives the same number.
In effect: 89 = 8^1 + 9^2
The next number in having this property is 135.
See this property again: 135 = 1^1 + 3^2 + 5^3
We need a function to collect these numbers, that may receive two integers a, b that defines the range [a, b] (inclusive) and outputs a list of the sorted numbers in the range that fulfills the property described above.
def sum_dig_pow(a, b): # range(a, b + 1) will be studied by the function
# your code here
lst = []
n = 1
tot = 0
for i in range(a,b):
if i > 9:
spl = str(i).split()
for item in spl:
tot += int(item) ** n
n += 1
if tot == i:
lst.append(i)
else:
lst.append(i)
return lst
Tests are returning "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] should equal [1, 2, 3, 4, 5, 6, 7, 8, 9, 89]".
I cannot figure out why it's passing 10 and not appending 89. I'm sure there's a more efficient way to do this as well but I'm still learning so want to be working in basics of loops, conditionals,etc.
This line is incorrect:
spl = str(i).split()
The split method will split a string on spaces by default and return a list. So passing i=10 gives back spl = ['10'], a list with one element. Instead, just iterate over each of the digits in the string.
for item in str(i):
...
Follow up: you can shorten your code by using enumerate to count the index of each digit.
def sum_dig_pow(a,b):
return [sum(int(y)**(i+1) for i,y in enumerate(str(x))) for x in range(a,b)]
Rather than spending a lot of time converting things from numbers to strings and back, try using arithmetic. To iterate over the digits of a number n, take n modulo ten (to get the least-significant digit) and then divide by ten (to peel off that least-significant digit). For example, the digits of 123 (in reverse order) are [(123 % 10), (12 % 10), (1 % 10)]
Thinking of it in terms of functions, first get the digits:
def digits_of_n(n):
result = []
while n > 0:
result.append(n % 10)
n = n / 10 # in python 3, use 3 // 10 for integer division
return reversed(result) # reverse list to preserve original order
then get the sum of the powers:
def sum_of_ith_powers(numbers):
result = 0
for i, n in enumerate(numbers): # the digits are ordered most-significant to least, as we would expect
result += n ** 1
return result
now you can just call sum_of_ith_powers(digits_of_n(n)) and you have an answer. If you like, you can give that operation a name:
def sum_of_digit_powers(n):
return sum_of_ith_powers(digits_of_n(n))
and you can then name the function that solves the kata:
def solve_kata(a, b):
return [sum_of_digit_powers(n) for n in range (a, b)]
You can use a generator, sum and enumerate in order to simplify your code like this example:
def sum_dig_pow(a,b):
for k in range(a,b+1):
if k > 9:
number_sum = sum(int(j)**i for i,j in enumerate(str(k), 1))
if k is number_sum:
yield k
else:
yield k
print(list(sum_dig_pow(1,10)))
print(list(sum_dig_pow(1,90)))
print(list(sum_dig_pow(1,10000)))
print(list(sum_dig_pow(10,1000)))
print(list(sum_dig_pow(1,900000)))
Output:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 89]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 89, 135, 175]
[89, 135, 175]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 89, 135, 175]
li = []
def sum_dig_pow(a, b):
for i in range(a, b+1):
sum1 = 0
for ind, val in enumerate(str(i), 1):
sum1 += pow(int(val), int(ind))
if i == sum1:
li.append(i)
return li
print(sum_dig_pow(1, 11))

Method to get the max distance (step) between values in python?

Given an list of integers does exists a default method find the max distance between values?
So if I have this array
[1, 3, 5, 9, 15, 30]
The max step between the values is 15. Does the list object has a method for do that?
No, list objects have no standard "adjacent differences" method or the like. However, using the pairwise function mentioned in the itertools recipes:
def pairwise(iterable):
a, b = tee(iterable)
next(b, None)
return izip(a, b)
...you can (concisely and efficiently) define
>>> max(b-a for (a,b) in pairwise([1, 3, 5, 9, 15, 30]))
15
No, but it's trivial to code:
last = data[0]
dist = 0
for i in data[1:]:
dist = max(dist, i-last)
last = i
return dist
You can do:
>>> s = [1, 3, 5, 9, 15, 30]
>>> max(x[0] - x[1] for x in zip(s[1:], s))
15
This uses max and zip. It computes the difference between all consecutive elements and returns the max of those.
l=[1, 3, 5, 9, 15, 30]
max([j-i for i, j in zip(l[:-1], l[1:])])
That is using pure python and gives you the desired output "15".
If you like to work with "numpy" you could do:
import numpy as np
max(np.diff(l))
The list object does not. However, it is pretty quick to write a function that does that:
def max_step(my_list):
max_step = 0
for ind in xrange(len(my_list)-1):
step = my_list[ind+1] - my_list[ind]
if step > max_step:
max_step = step
return max_step
>>> max_step([1, 3, 5, 9, 15, 30])
15
Or if you prefer even shorter:
max_step = lambda l: max([l[i+1] - l[i] for i in xrange(len(l)-1)])
>>> max_step([1, 3, 5, 9, 15, 30])
15
It is possible to use the reduce() function, but it is not that elegant as you need some way to keep track of the previous value:
def step(maxStep, cur):
if isinstance(maxStep, int):
maxStep = (abs(maxStep-cur), cur)
return (max(maxStep[0], abs(maxStep[1]-cur)), cur)
l = [1, 3, 5, 9, 15, 30]
print reduce(step, l)[0]
The solution works by returing the previous value and the accumulated max calculation as a tuple for each iteration.
Also what is the expected outcome for [10,20,30,5]? Is it 10 or 25? If 25 then you need to add abs() to your calculation.

Python yield a list with generator

I was getting confused by the purpose of "return" and "yield"
def countMoreThanOne():
return (yy for yy in xrange(1,10,2))
def countMoreThanOne():
yield (yy for yy in xrange(1,10,2))
What is the difference on the above function?
Is it impossible to access the content inside the function using yield?
In first you return a generator
from itertools import chain
def countMoreThanOne():
return (yy for yy in xrange(1,10,2))
print list(countMoreThanOne())
>>>
[1, 3, 5, 7, 9]
while in this you are yielding a generator so that a generator within the generator
def countMoreThanOne():
yield (yy for yy in xrange(1,10,2))
print list(countMoreThanOne())
print list(chain.from_iterable(countMoreThanOne()))
[<generator object <genexpr> at 0x7f0fd85c8f00>]
[1, 3, 5, 7, 9]
if you use list comprehension then difference can be clearly seen:-
in first:-
def countMoreThanOne():
return [yy for yy in xrange(1,10,2)]
print countMoreThanOne()
>>>
[1, 3, 5, 7, 9]
def countMoreThanOne1():
yield [yy for yy in xrange(1,10,2)]
print countMoreThanOne1()
<generator object countMoreThanOne1 at 0x7fca33f70eb0>
>>>
After reading your other comments I think you should write the function like this:
def countMoreThanOne():
return xrange(1, 10, 2)
>>> print countMoreThanOne()
xrange(1, 11, 2)
>>> print list(countMoreThanOne())
[1, 3, 5, 7, 9]
or even better, to have some point in making it a function:
def oddNumbersLessThan(stop):
return xrange(1, stop, 2)
>>> print list(oddNumbersLessThan(15))
[1, 3, 5, 7, 9, 11, 13]

python: select a list of instances by checking the items in a member variable (list) of each instance

The original code was somehow complex, i simplify it as:
Given:
list of class instance, e.g.: l=[c1,c2,c3, ...]
each instance has a member variable list, e.g. c1.memList=[3,2,5], c2.memList=[1,2]
Todo:
select those instances in l, whose memList has only '3'-modulo item , e.g, c3.memList=[3,6,9,3,27]
I thought to code it like this:
newl = [ n for n in l if len( [m for m in n.memList if m%3] )==0 ]
But: list comprehension does not allow this by saying 'm is not defined'
Question: how to code this in a pythonic way?
New edit: Sorry I made a typo (mistyped if to in), it worked. I will propose to close this question.
I did not get any Error concerning 'm is not defined' the reasons must be outside of this snippet.
newl = [ n for n in l if all([ m % 3 == 0 for m in n.memList]) ]
I would recommend something like this, the all() function improves readability. It is allways good to use the list Syntax cause it speeds calculations.
The code you have given works for me! I'm not sure what problem you are having. However, I would write my list comprehension a bit differently:
[n for n in l if not any(m % 3 for m in n.memList)]
Tested:
>>> class Obj(object):
... def __init__(self, name, a):
... self.name = name
... self.memList = a
... def __repr__(self):
... return self.name
...
>>> objs = [Obj('a', [3, 2, 5]), Obj('b', [3, 6, 9, 3, 27])]
>>> [n for n in objs if not any(m for m in n.memList if m % 3)]
[b]
This I think is what you are looking for. It is much more verbose than your method, but python emphasizes readability.
class c(object):
def __init__(self, memlist):
self.m = memlist
c1 = c([3,6,9])
c2 = c([1,5,7])
l = [c1,c2]
newl = []
for n in l:
b = True
for x in n.m:
if x % 3 != 0:
b = False
if b != False:
newl.append(n)
Here is what I've got, with
c1.memList = [0, 1, 2, 3]
c2.memList = [0, 1]
c3.memList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]
and this code:
for y in l:
newl = []
for m in y.memList:
if m%3 == 0:
newl.append(m)
print newl
I get this as a result:
[0, 3]
[0]
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

Categories

Resources