Printing family tree until certain level | Python 3 - python-3.x

I am struggling with a recursive function that prints a family tree until a certain "depth/level".
I have defined class "Person" and each person has some descendant(s), so lets say:
>>> george.children
[<__main__.Person object at 0x000002C85FB45A58>]
>>> george.name
'George'
And I want to print the family tree in a way that each generation is separated by 4 whitespaces, for example:
>>> family_tree(george, level = 2)
George
Michael
Juliet
Peter
Mary
George is the level 0, then his two sons are level 1, etc.
Do you please have any ideas how to write this using recursion? I would greatly appreciate it.

You could use recursion. At each deeper level of recursion you should produce 4 more spaces. So for that purpose you could pass an argument depth that is increased at every recursive call.
Here is how you could do it:
# You'll have a class like this:
class Person:
def __init__(self, name):
self.name = name
self.children = []
def addChild(self, child):
self.children.append(child)
return self # to allow chaining
# The function of interest:
def family_tree(person, level = 2):
def recurse(person, depth):
if depth > level: return
print (" " * (4 * depth) + person.name)
for child in person.children:
recurse(child, depth+1)
recurse(person, 0)
# Sample data
george = Person("George").addChild(
Person("Michael").addChild(
Person("Juliet").addChild(
Person("don't show me")
)
)
).addChild(
Person("Peter").addChild(
Person("Mary")
)
)
# Call the function
family_tree(george)

Related

AttributeError Problem with Multiple inheritance in python

I wanted to calculate the Total and Average Marks of a student with multiple inheritances in python. But whenever I create an object for my child class it gives me access to all the methods of parent classes but shows an AttributeError when I call the method of the child class. I tried to use the super() function but the result is the same.
I just paste my code below. Can you suggest to me a solution to that?
class Student_Marks:
def __init__(self):
# initializing variables
self.__roll: int
self.__marks1: int
self.__marks2: int
def getMarks(self):
self.__roll = int(input("Enter Roll No: "))
self.__marks1, self.__marks2 = map(int, input("Enter Marks: ").split())
return self.__roll, self.__marks1, self.__marks2
class Cocurricular_Marks:
def __init__(self):
self.__activemarks: int
def getActiveMarks(self):
self.__activemarks = int(input("Enter Co Curricular Activities Marks: "))
return self.__activemarks
class Result(Student_Marks, Cocurricular_Marks):
def __init__(self):
super().getMarks()
super().getActiveMarks()
def display(self):
total = self.__marks1 + self.__marks2 + self.__activemarks
avg = total / 3
print("Roll No: ", self.__roll)
print("Total Marks: ", total)
print("Average Marks: ", avg )
# creating Objects
res = Result()
res.getMarks()
res.getActiveMarks()
res.display() # I got problem here
You're prepending the attributes with two underscores in the classes, this mangles the name of the attribute (see the documentation on Private Variables).
For instance, Student_Marks's __roll will be mangled to _Student_Marks__roll as you exit getMarks.
Hence Result.display() not being able to access self.__marks1, but it can access it as self._Student_Marks__marks1.
See the following minimal example.
class K:
__a = 1
class KK(K):
pass
k = K()
k.__a # AttributeError: 'K' object has no attribute '__a'
k._K__a # 1
kk = KK()
kk._K__a # 1

Why class atribute doesn't change on update

I am wondering why the class attribute doesn't change in the code below. As you can see the value remains the same, despite being changed in class A?
class A:
valueA = 1.05
class User:
def __init__(self,name):
self.name = name
self.value = A.valueA
user = User('Alice')
print(user.value)
A.valueA = 1.1
print(A.valueA)
print(user.value)
output:
1.05
1.1
1.05
I don't understand why it should? ValueA is an number which is an immutable object(everything is a object), so valueA is just some sort of referencethat points to value 1.05.
To make more clear, here is an example of how does it behave:
class A(object):
val = [1,2,3]
class B(object):
def __init__(self):
self.myval = A.val
print(A.val)
# prints [1,2,3]
obj = B()
print(obj.myval)
# prints [1,2,3] because its starts to point to the same list
A.val[0] = 5
print(obj.myval)
# prints [5,2,3] because both still points to the same list,
# and you just changed it fist value
A.val = [4,5,6,7]
print(A.val)
# prints new list [4,5,6,7]
print(obj.myval)
# prints [5,2,3] because it still points to old list.
obj2 = B()
print(obj2.myval)
# prints new list [4,5,6,7] because assignment was done after A.val changed
also here is a good article about variables in python https://realpython.com/python-variables/#object-references

Write class such that calling instance returns all instance variables

I have answered my own question - see answer below
I'm writing a class, and I want this behavior:
a = f(10,20)
some_funct(a.row) # some_function is given 10
some_funct(a.col) # some_function is given 20
some_funct(a) # some_function is given a tuple of 10, 20 <-- THIS ONE :)
The last behavior is stumping me. I have not seen any examples that cover this.
Thus far:
class f(object):
"""Simple 2d object"""
row: int
col: int
def __init__(self, row, col):
self.row = row
self.col = col
Explictly I do not want another method, say, self.both = row, col.
I just want to 'call' the instance
I'm new to classes, so any improvements are welcome. Properties, setters, getters etc.
EDIT 1:
Replaced "print" with "some_function" in the question, and modified title
You can do like this
class f(object):
"""Simple 2d object"""
row: int
col: int
def __init__(self, row, col):
self.row = row
self.col = col
def __str__(self):
return f"row = {row}, col = {col}"
and print like this
a = f(10,20)
print(a) # row = 10, col = 20
This might help
class f(object):
"""Simple 2d object"""
row: int
col: int
def __init__(self, row, col):
self.row = row
self.col = col
def some_funct(self):
return (self.row, self.col)
You can access like
a = f(10,20)
a.some_funct() # (10, 20)
# or
row, col = a.some_funct()
From python 3.7 dataclasses have been introduced and their goal is to create classes that mainly contains data. Dataclasses comes with some helper function that extract the class attributes dict/tuples. e.g.
from dataclasses import dataclass,asdict,astuple
#dataclass
class f:
x: int
y: int
f_instance = f(10,20)
asdict(f_instance) # --> {'x': 10, 'y': 20}
astuple(f_instance) # -> (10,20)
EDIT I : Another technique would be to use namedtuple e.g.:
from collections import namedtuple
f = namedtuple('p',['row','col'])
a =f(10,20)
a.row #-> 10
a.col #-> 20
class f(tuple):
"""Simple 2d object"""
def __new__(cls, x, y):
return tuple.__new__(f, (x, y))
def __init__(self, x, y):
self.col = x
self.row = y
foo = f(1,2)
print(foo.col)
>>>1
print(foo.row)
>>>2
print(foo)
>>>(1, 2)
Importantly:
If you want it to behave like a tuple then make it a subclass of tuple.
Much stuffing around but stumbled upon an external site which gave me clues about the keywords to search on here. The SO question is here but I have modified that answer slightly.
I'm still a little confused because the other site says to use new in the init as well but does not give a clear example.

Recursive function with kwargs

I want a function that takes in kwargs to recursively. How can I pass the kwargs on?
example of the code:
def recursion(a, b, **kwargs):
if a == 1:
print(a + b)
elif a == 2:
print(a + b + kwargs['name']
else:
a = a/2
recursion(what to put in here?)
def re(a, b, **kwargs):
print(a + b, kwargs['name'])
if a == 0:
return
else:
re(a-b,b,**kwargs)
re(5,1,name='Hello World!')
This will give you the following output
6 Hello World!
5 Hello World!
4 Hello World!
3 Hello World!
2 Hello World!
1 Hello World!
Having been faced with a similar problem recently, I've come up with the following:
from functools import wraps
from inspect import currentframe
def recursive_kwargs(fun):
''' Persist keyword arguments through recursive calls '''
#wraps(fun)
def wrapper(*args,**kwargs):
caller_name = currentframe().f_back.f_code.co_name
if caller_name is fun.__name__:
fun(*args,**fun.__kwargs__)
else: # Top of the recursive stack
fun.__kwargs__ = kwargs
fun(*args,**kwargs)
return wrapper
You can then just decorate your function and presto, it ought to work by itself without passing any kwargs along:
#recursive_kwargs
def fib(n, codename='spam', password='eggs'):
print(codename, password)
if n == 1:
return 1
return fib(n-1) # Don't pass any kwargs
Now fib(3) returns spam eggs (3 times), and fib(3,codename='Vikings') does indeed return Vikings eggs (3 times).
This is lovely and convenient, but SLOW, both due to the #wraps and the currentframe lookup. Here are some timeit results:
fib: [3.69, 3.24, 2.82, 2.57, 2.56] us # As defined above
fib2: [1.45, 1.14, 1.15, 1.08, 1.08] us # With just the #wraps (but doing nothing)
fib3: [0.58, 0.66, 0.68, 0.54, 0.48] us # Just passing the kwargs along manually
TLDR: #Harish's answer is probably the way to do this unless you don't care about efficiency. For example if you are dealing with a shallow (constant depth, even) recursion stack where the bottleneck is the body of the function itself, not the recursion (aka definitely not the example I've used here).
Also; don't use recursion without memoisation for calculating Fibonacci numbers (see e.g. here for more information)

How to separate two concatenaded words

I have a review dataset and I want to process it using NLP techniques. I did all the preprocessing stages (remove stop words, stemming, etc.). My problem is that there are some words, which are connected to each other and my function doesn't understand those. Here is an example:
Great services. I had a nicemeal and I love it a lot.
How can I correct it from nicemeal to nice meal?
Peter Norvig has a nice solution to the word segmentation problem that you are encountering. Long story short, he uses a large dataset of word (and bigram) frequencies and some dynamic programming to split long strings of connected words into their most likely segmentation.
You download the zip file with the source code and the word frequencies and adapt it to your use case. Here is the relevant bit, for completeness.
def memo(f):
"Memoize function f."
table = {}
def fmemo(*args):
if args not in table:
table[args] = f(*args)
return table[args]
fmemo.memo = table
return fmemo
#memo
def segment(text):
"Return a list of words that is the best segmentation of text."
if not text: return []
candidates = ([first]+segment(rem) for first,rem in splits(text))
return max(candidates, key=Pwords)
def splits(text, L=20):
"Return a list of all possible (first, rem) pairs, len(first)<=L."
return [(text[:i+1], text[i+1:])
for i in range(min(len(text), L))]
def Pwords(words):
"The Naive Bayes probability of a sequence of words."
return product(Pw(w) for w in words)
#### Support functions (p. 224)
def product(nums):
"Return the product of a sequence of numbers."
return reduce(operator.mul, nums, 1)
class Pdist(dict):
"A probability distribution estimated from counts in datafile."
def __init__(self, data=[], N=None, missingfn=None):
for key,count in data:
self[key] = self.get(key, 0) + int(count)
self.N = float(N or sum(self.itervalues()))
self.missingfn = missingfn or (lambda k, N: 1./N)
def __call__(self, key):
if key in self: return self[key]/self.N
else: return self.missingfn(key, self.N)
def datafile(name, sep='\t'):
"Read key,value pairs from file."
for line in file(name):
yield line.split(sep)
def avoid_long_words(key, N):
"Estimate the probability of an unknown word."
return 10./(N * 10**len(key))
N = 1024908267229 ## Number of tokens
Pw = Pdist(datafile('count_1w.txt'), N, avoid_long_words)
You can also use the segment2 method as it uses bigrams and is much more accurate.

Resources