Printing part of object's string - python

Let's say I have a class like this:
class Cls:
def __init__(self, a,b,c):
self.a = a
self.b = b
self.c = c
def __str__(self):
return f"{self.a} {self.b} {self.c}"
and I want to print only the first two items. I can do this by brute force:
c = Cls(1,2,3)
print(str(c)[:3])
But, is there a more elegant (Pythonic) way to do this?

Your slice only works if the first 2 items are only 1 digit each.
You should split the string into a list using whitespace delimiters and slice that.
c = Cls(12, 345, 6)
print(*str(c).split()[:2])

Related

Jsonpickle - ensure deterministic encoding

The encoding is basically string representation of a dictionary, containing the object's fields. However, a dictionary does not respect order, and I could potentially get different encoding string on different runs. How do I preclude this from happening? Or should I use another library where I can ensure deterministic encoding?
By deterministic encoding, I mean if I create 100000 objects that are practically the same, i.e. same class and same constructor args, when I call encode() on each one of them, I get the exact same string every time.
So, for example, if I have
class MyClass(object):
def __init__(self, a, b):
self.a = a
self.b = b
c1 = MyClass(1, 2)
c2 = MyClass(1, 2)
I want to be sure that the strings encode(c1) and encode(c2) are perfectly identical, character for character, i.e.
assert jsonpickle.encode(c1)==jsonpickle.encode(c2)
I think that jsonpickle will take care of what you call determininstic endocing.
Example
import jsonpickle
class Monopoly(object):
def __init__(self):
self.boardwalk_price = 500
#property
def boardwalk(self):
self.boardwalk_price += 50
return self.boardwalk_price
m = Monopoly()
serialized = jsonpickle.encode(m)
Take a look at
print (serialized)
{"py/object": "__main__.Monopoly", "boardwalk_price": 500}
Now, let's decode
d = jsonpickle.decode(serialized)
print (d)
<__main__.Monopoly object at 0x7f01bc093278>
d.boardwalk_price
500
For comparing objects,Python uses identifiers.
class MyClass(object):
def __init__(self, a, b):
self.a = a
self.b = b
c1 = MyClass(1, 2)
c2 = MyClass(1, 2)
If you take a look at id
id(c1)
140154854189040
id(c2)
140154854190440
c1 == c2
False
You can override eq operator
def __eq__(self, x):
if isinstance(x, number):
return self.number == x.number
return False

Counting the occurence of instances of a certain class in list of lists

I have list of lists in which I want to count the number of B() and C() instances and am looking for a suitable method to do this. Using collections.Counter() and the .count() function have resulted in strange results, and I suspect I do not fully understand how list of lists work in python, or how lists of class instances work in python.
This is the list of lists:
lst = [[B() for w in range(x)] for h in range(y)]
with
class A():
def __init__(self, name):
self.name = name
class B(A):
def __init__(self, name = "B"):
A.__init__(self, name)
def update(self):
if random.random() < 0.05:
return C()
else: return self
class C(A):
def __init__(self, name = "C"):
A.__init__(self, name)
And, I use the below code to randomly change B() instances in lst into C() instances:
for row in range(y):
for column in range(x):
lst[row][column] = lst[row][column].update()
How do I count the number of B() and C() instances in the list?
You can use isinstance()
You can check what class an element is with isinstance().
Here is an example:
>>> a = C()
>>> isinstance(a, C)
True
So if you have your list, you can do:
occurrences_of_B = sum(isinstance(i, B) for r in list for i in r)
occurrences_of_C = sum(isinstance(i, C) for r in list for i in r)
you can get the occurrences of the B() and C() classes.
Essentially, we are using a generator comprehension to apply the isinstance() function to every element in the list. We then use sum on the generator as True evaluates to 1 and False to 0, so we will get the total count.
As a side note, although I said it is not good practice to name a list 'array', it is actually worse to name it exactly 'list' as this prevents you from being able to use the list() function! Better would probably be lst or l. :)

How to dynamically create classes

I am relatively new to Python and am asking myself if it is possible to create classes where names are dynamically created within a function?
Code:
def create_dummy_elements():
rows = [A,B,C,D,E,F,G,H,I]
columns = [A,B,C,D,E,F,G,H,I]
for r in range (rows):
for c in range (columns):
***** = element(1,1,1,False)
My result should be 81 objects of the class "element" named like AA,AB,AC,AD, ...
* is what I am actually asking for ...
You can make a list of these objects, but it's not ideal to create variable names dynamically.
Something like this:
my_list_of_elements = []
for r in range (rows):
for c in range (columns):
my_list_of_elements.append(element(1,1,1,False))
Then you can access them by index number, for example: my_list_of_elements[n]
or to match the two-dimensional style for loops you have:
my_list_of_elements = []
for r in range (rows):
temp = []
for c in range (columns):
temp.append(element(1,1,1,False))
my_list_of_elements.append(temp)
then you can do my_list_of_elements[i][j] to access the i-th row and the j-th column.
If you prefer a string index, a dictionary would serve you well:
my_dict_of_elements = {}
for r in range (rows):
for c in range (columns):
my_dict_of_elements["element"+(r*c+c)] = element(1,1,1,False)
which will give you access like this my_dict_of_elements["element0"] for example.
As mentioned in the comment by atomicinf on this post, you can use the globals() dict, but it seems we both agree there are better practices.
I think you can just can create list as suggested farmerjoe, but if you really want to add namespases to global, you can do it like
class element(object):
def __init__(self, *args):
self._args = args
def __repr__(self):
return ' '.join(map(str, self._args))
rows = ['A','B']
columns = ['A','B']
for r in rows:
for c in columns:
exec('%s%s = element(r,c,False)' % (r,c))
print AB
output will be
A B False
Although it's possible to create classes and named instance of classes dynamically in Python, doing so is generally frowned upon. It's considered more "Pythonic" and elegant to store collections of such things in a container of some sort such as a list or dictionary, and then access them when necessary by index or key. Here's how that might apply to your problem:
class Element(object):
def __init__(self, name, a=None, b=None, c=None):
self.name, self.a, self.b, self.c = name, a, b, c
def __str__(self):
classname = self.__class__.__name__
return('{classname}('
'name={self.name}, b={self.a}, '
'c={self.b}, d={self.c})'.format(classname=classname,
self=self))
board = {} # empty dictionary
for row in 'ABCDEFGHI':
for col in 'ABCDEFGHI':
name = row+col
board[name] = Element(name) # assign a named Element instance
# sample usage
board['AB'].a = 42
print board['AB'] # Element(name=AB, a=42, b=None, c=None)
board['GD'].c = 'something'
print board['GD'] # Element(name=GD, a=None, b=None, c=something)
Note: I don't really know what to call most of the attributes of the Element class, so am just using a, b, and c in the above for illustration purposes.

Python, mutable object as default argument, is there any way to solve?

class A:
def __init__(self, n=[0]):
self.data = n
a = A()
print a.data[0] #print 0
a.data[0] +=1
b = A()
print a.data[0] #print 1, desired output is 0
In the case above, is there any way to provide a default argument with the mutable object (such as list or class) in __init__() class A, but b is not affected by the operation a?
You could try this:
class A:
def __init__(self, n=None):
if n is None:
n = [0]
self.data = n
Which avoids the biggest problem you're facing here, that is, that's the same list for every single object of your type "A."
One possibility is:
class A:
def __init__(self, n=None):
if n is None:
n = [0]
self.data = n
Also:
class A:
def __init__(self, n=[0]):
print id(n)
self.data = n[:]
print id(self.data)
del n
a = A()
print a.data[0] #prints 0
a.data[0] +=1
print a.data[0] #prints 1
print
b = A()
print b.data[0] #prints desired output 0
The principle is that it creates another list. If a long list is passed as argument, there will be two long list in memory. So the inconvenience is that it creates another list.... That's why I delete n.
Don't think it's better, but it may give you comprehension of what happens

how to enumerate class method then chain them with itertools.product() in python?

I just learned yesterday from this site that I can:
class Seq(object):
def __init__(self, seq):
self.seq = seq
def __repr__(self):
return repr(self.seq)
def __str__(self):
return str(self.seq)
def all(self):
return Seq(self.seq[:])
def head(self, count):
return Seq(self.seq[:count])
def tail(self, count):
return Seq(self.seq[-count:])
def odd(self):
return Seq(self.seq[1::2])
def even(self):
return Seq(self.seq[::2])
def reverse(self):
return Seq(self.seq[::-1])
>>> s = Seq(range(0, 100))
>>> print s.head(10).odd().even().reverse()
[9, 5, 1]
I want to enumerate possible combination of those sequence method chains inside of class Seq, may sort of:
itertools.product([s.head,s.odd,s.even,s.reverse], repeat=4)
# may not just limited those 4 functions
how to use the itertools.product() to
1). generate invoke-able function chains list? just like this:
foo = s.head().odd().even().reverse()
2). generate eval()able chain strings then I can store in ascii file or eval() later or for logging purpose?
the head(), tail() may accept parameter, and even(), odd() is not need to, for example,
the paremeter of head() and tail() may from lists:
head_lmt = [10,20,30]
tail_lmt = [30,40,50]
foo = s.head().odd().tail().reverse()
^------------------------------------head_lmt 10 or 20 or 30
^-----------------------tail_lmt 30 or 40 or 50
If my Q1 is possible, how I can fill those parameter into the invoke-able list and the eval()-able string, a.k.a generate more specific invoke-able list and the eval()-able string?
Thanks!
Note that something like "s.head()" means a method which is "bound" to that specific instance of Seq, that is, "s." Something like "Seq.head()" means a method which is unbound, so one can still pass in different instances of Seq.
From there it simply requires basic functional composition and string concatenation.
def chain_method(from_method, to_method):
def inner(arg):
return to_method(from_method(arg))
return inner
possible_funcs = []
log_strings = []
for possible_combo in itertools.product([Seq.head,Seq.odd,Seq.even,Seq.reverse], repeat=4):
meta_method = possible_combo[0]
for method in possible_combo[1:]:
meta_method = chain_method(meta_method, method)
log_string = []
for method in possible_combo:
log_string.extend(['.', method.__name__, '()'])
possible_funcs.append(meta_method)
log_strings.append("".join(log_string))
I'm not sure what you mean by the examples for the additional parameters, though. How do you intend to combine the different parameter values with the different combinations of functions?

Categories