<jupyter_start><jupyter_text>**study python**:)\---https://docs.python.org/3/tutorial/introduction.html<jupyter_code># Clear all variables
%reset -f

import sys
print('Python ver', sys.version)
# 3.1.2. Strings

print('C:\some\name')  # here \n means newline!
print(r'C:\some\name')  # a raw string. note the r before the quote

# span multiple lines by using """...""" or '''...''' . End of lines are automatically
# included, but it’s possible to prevent this by adding a \ at the end of the line.
print('''\
Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to
ignore_new_line_by\
\ ignore_new_line_test \t
\t end #comment?
''')

# 3 times 'un', followed by 'ium'
print(3*'un.'+'ium')
print(2* 'str1' 'str2'  # More string literals are automatically concatenated. It is useful for breaking long strings.
 'str3' )               # This only works with two literals though, not with variables or expressions.

# sub string
word = '0123456789'
l = len(word)
print('len=' , l, '\n',
word[-1]+'=', word[-1] == word[l-1] ,'\n',
word[-2]+'=', word[-2] == word[l-2])

print(word[:3] + '+' + word[3:])
print(word[:-3] + '+' + word[-3:])
print(word[:13] + '+' + word[13:]) #  out of range slice indexes are handled gracefully 
print(word[2:3] + '=' + word[2])
# Lists
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
letters.append('XYZ')
letters.append(123)

letters[2:5] = ['C', 'D', 'E'] # replace some values
print(letters)

letters[2:5] = [] # now remove them
print(letters)

letters[-1] = [0,1,2,3,4,5]       # nest lists
letters[-1][-3]=['3a','3b','3c']
print(letters)

letters[:] = [] # clear the list by replacing all the elements with an empty list
print(letters)

print('None test', 22*'-')

print('\'==\' is for value equality. \n\'is\' is for reference equality.' )
p, q = None, None   # None is a singleton object
print(p, 'is', q, '?', p is q) # True because they are both pointing to the same "None"
print(p, '==', q, '?', p == q) # Of course True. Also, 'is' is a bit (~50%) faster than '==' because it compare reference.

p, q = [1], [1]
print(p, 'is', q, '?', p is q) # False because they are not the same actual object
print(p, '==', q, '?', p == q) # True because they are equivalent

print(9*'-')
def if_test(x):
  print('T=', x == True, ',F=', x==False, ',N=' ,x==None, ',(yes)' if x else ',(no)', ':', x, type(x))
  
if_test(True)
if_test(False)
if_test(None)

print('---')
if_test(0)    # ==False
if_test(1)    # ==True
if_test(0.0)  # ==False
if_test(1.1)  # !=True

print('---')
if_test([])
if_test([3])
if_test('')
if_test('3')

print(9*'-')
print('xxx' or 'or')
print(True or 'or')
print(None or 'or')
print(False or 'or')

print(9*'-')
print('xxx' and 'and')
print(True and 'and')
print(None and 'and')
print(False and 'and')


print('Tuple to string', 22*'-')
tuple = True, False, None, 1, 2, 'apple'
print(tuple)
print(':'.join(map(str, tuple)))
print(':'.join(map(lambda x: str(x or '?'), tuple)))


def fib(n):
  """Print a Fibonacci series up to n.""" 
  # The 1st str is a docstring that can produce doc or let users browse the code.
  
  print('Fibonacci: ', end='')
  a, b = 0, 1
  c, d = 0, 1
  while a < n:
      print(a , end=',') # The argument end can be used to avoid the newline after the output
      if a!=c: raise Exception(a, "!=", c)
      a, b = b, a+b   # b, a = a+b, b # works too
      t=c; c=d; d=c+t # it works too.
  print() # print \n
  # functions without a return statement do return a value 'None'

print('docstring=', fib.__doc__)  # show docstring
fib(3333)
print(fib(3333))  # it will print 'None' because fib returns 'None'

print('\n----- dict -----')
users = {'Hans': 1, 'Éléonore': 0, '景太郎': 1} #dict
tmp = users # a reference, not a copy.
users['景太'] = 1
users['XXX'] = 3
del tmp['XXX']
print('dict reference assign test', tmp, users, tmp==users,'---', sep=' ;\n') # use 'sep' to change separator
for user, status in users.items(): 
  if status != 0:
     print('(',user,status, end=') ')
print('ok')

print('\n----- tuple & dict -----')
tup = 1,'2',[3,4,5],(6,7,8,9,0),{'a','b','c'} # A tuple is a immutable list.
print(type(tup), tup)

users = ('Hans',1), ('Éléonore',0), ('景太',1), ('吉胖喵',0), ('小石獅',0) # It can omit the outermost parentheses
res1 = dict(users)
res2 = []
for user,status in users:
    if status == 0:
      del res1[user]
    else:
      res2.append((user,status))

result1 = list(res1.items()) # convert dict to list
print(users, result1, res2, result1==res2, sep='\n')

print('\n----- range & map -----')
# class range(start, stop[, step=1])
# class range(stop) -> range(0, stop)
rgs = [range(5),range(5,10),range(0, 10, 3),range(-11, -100, -22)]
rg_lst = tuple(map(lambda r,u:(u[0], r, tuple(r), sum(r)), rgs, users)) # merge users and ranges to tuple by map()
print(*rg_lst, sep='\n') # unpack lists by '*'
print('ranges=',len(rgs),', users=',len(users),', result=',len(rg_lst)) # len(rg_lst) = min(len(rgs), len(users))


# function

while False:
  pass   # statement is required but the program requires no action.

class MyEmptyClass: # a minimal classes:
  pass

class Test:
  def __init__(self):
    self.func_data='func'
    self.prop_data='prop'

  def func(self):
    return self.func_data

  @property
  def prop(self):
    return self.prop_data

t=Test()
print('invoke class property vs function:', t.func(),t.prop)


def fn_foo(*args):
  pass   # Remember to implement this! ( a place-holder for a function or conditional body )

print('\n----- find prime numbers -----')
for n in range(2, 10):
  for x in range(2, n):
    if n % x == 0:
      print(n, 'no', end=', ')
      break
  else:  # the else clause belongs to the for loop, not the if statement.
    print(n, 'yes', end=', ') # not be executed when the for/while loop is terminated by a break
print('done...')

# *iterable unpacking operator and ** dictionary unpacking operator
# https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations
print('\n----- Unpacking -----')
print(*[1,2], 3, *[4, 5])
print(*range(4),*[ 4, *(5, 6, 7)])
d={'x':1, **{'y': 2, 'z':3}}
print(d)
print(*d)

# print(**d) #TypeError: 'x' is an invalid keyword argument for print()

print('----- Passing dictionary as keyword arguments -----')
kwargs0 = dict(b=2, d=4)
kwargs1 = {'b':2, 'd':4}
print(kwargs0, kwargs1, kwargs0==kwargs1) # the same

def fn(a, b, c, d):
    print('fn:', a, b, c, d)

fn(*(1,2,3,4))
fn(a=1, **kwargs0, c=3)
fn(**{'a': 1, 'c': 3}, **kwargs1)

print('----- keyword arguments in function def -----')

# Any formal parameters which occur after the *args parameter are ‘keyword-only’ arguments
def g(pos, *args, kw_only, default='DAFAULT', **kwargs): # *name must occur before **name. 
  print('pos=',pos, 'kw_only=', kw_only, 'default=', default)
  print('args=', type(args), args)
  print('kwargs=', type(kwargs), kwargs)
  print('-'*22)
  fn(**kwargs)
  
g('POS', 1,2,3,4, a=5,b=6,c=7,d=8, kw_only='ARG')
# g(1,2,3,4, a=5,b=6,c=7,d=8, kw_only='ARG', pos='POS') # TypeError: g() got multiple values for argument 'pos'
# https://docs.python.org/3.8/tutorial/controlflow.html#special-parameters
print('----- position and keyword only argument -----')

def pos_kw_only_test():
  pass # These need verson > 3.8
  # def pos_only(arg, /):
  #     print(arg)
  # pos_only(1)
  # # pos_only(arg=0) # SyntaxError: invalid syntax

  # def kwd_only(*, arg):
  #     print(arg)
  # kwd_only(arg=1)
  # # kwd_only(1) # SyntaxError: invalid syntax

  # def combined(pos_only, /, standard, *, kwd_only):
  #     print(pos_only, standard, kwd_only)

  # combined(1, 2, 3)
  # combined(1, 2, kwd_only=3)
  # combined(1, standard=2, kwd_only=3)
  # combined(pos_only=1, standard=2, kwd_only=3)

#------------------------------------

def tval(v): return type(v),v

def str2tuple(ver):
  return tuple(map(int, ver.split('.')))

print(tval(str2tuple('3.8.9')))

import sys
print(tval(sys.version_info), '\n', tval(sys.version))

ver = sys.version_info[:3]
print(tval(ver))

target_ver = 3,8,0
if ver > target_ver:
  pos_kw_only_test()
else:
  err_str = "".join(map(str, ('not support!', ver, '<', target_ver)))
  print(err_str)
  # raise Exception(err_str)
  # assert(False)
  # raise # RuntimeError: No active exception to reraise


print('lambda test:')
def tval(v): return type(v),v

def make_incrementor(n):
    return lambda x: int(x) + int(n)

f = make_incrementor(33)
print(type(f), f)
print(f(3))
print(f('3'))


f = make_incrementor('22')
print(type(f), f)
print(f(2))
print(f('2'))

print('-' * 22)

print('annotations test:')

def f(ham: str, eggs: str = 'eggs') -> str:
    print("Annotations:", f.__annotations__)
    print("Arguments:", ham, eggs)
    return str(ham) + ' and ' + eggs

print(f('spam'),'\n')
print(f(10),'\n') # OK, It doesn't check types by annotations

print('without annotations', '-' * 9)

def f(ham, eggs = 'eggs'):
    print("Annotations:", f.__annotations__)
    print("Arguments:", ham, eggs)
    return ham + ' and ' + eggs

print(f('spam'))
for i, v in enumerate(['a', 'b', 'c']):   # loop with index
  print(i, v)

basket = ['apple','apple','apple', 'orange', 'apple', 'pear', 'orange']
print('sort set:', sorted(set(basket)))  # sorted() function which returns a new sorted list while leaving the source unaltered. 
  
print('filter:', tuple(filter(lambda x: len(x)<6, basket)))

a,b,c=1,3,3
print( a<b==c )   # chained comparisons. tests whether a is less than b and moreover b equals c.
print( (a<b)==c )
c=True
print( (a<b)==c )

# The Boolean operators 'and' & 'or' are short-circuit operators, 
print('\nThe return value of a short-circuit operator is the last evaluated argument.')
a, b, c = 'aaa','','bbb'
print('or', a or b or c)
print('and', a and b and c)

a, b, c = True,False,True
print('or', a or b or c)
print('and', a and b and c)

print('\nComparing test')
print((1, 2, 3)              < (1, 2, 4))
print([1, 2, 3]              < [1, 2, 4])
print('ABC' < 'C' < 'Pascal' < 'Python')
print((1, 2, 3, 4)           < (1, 2, 4))
print((1, 2)                 < (1, 2, -99)) # none < '-99'
print((1, 2, 3)             == (1.0, 2.0, 3.0))
print((1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4))
# Python 3.10 supports match Statement (switch case)

# def http_error(status):
#   match status:
#     case 400:
#       return "Bad request"
#     case _:
#       return "Something's wrong with the internet"

print('fn name=',__name__)
if __name__ == "__main__":
    import sys
    print('argv:', sys.argv)

# sys.ps1 and sys.ps2 define the strings used as primary and secondary prompts. 
# They are defined if the interpreter is in interactive mode.
print('sys.ps:', sys.ps1, '&',sys.ps2)
    
# sys.path=search path for modules. default=environment variable PYTHONPATH, or from a built-in default.
print('sys.path=', sys.path)
# sys.path.append('/ufs/guido/lib/python') # add a path

sys.ps1 = 'C> '

def ask_ok(prompt, retries=3, reminder='Please try again!'):
    while True:
        ok = input(prompt)
        if ok in ('y', 'ye', 'yes'):
            return True
        if ok in ('n', 'no', 'nop', 'nope'):
            return False
        retries = retries - 1
        if retries < 0: raise ValueError('invalid user response')
        print(reminder)

print('fn name:', ask_ok.__name__)
ask_ok('yes or no? ')
print('''Note that when using from package import item, the item can be
either a submodule (or subpackage) of the package, or some other
name defined in the package, like a function, class or variable.\n''')

import test.support
print(test.support.fd_count())

from test import support
print(support.fd_count())

from test.support import fd_count
print(fd_count())

from test.support import fd_count as xxx
print(xxx())
# Fancier Output Formatting

yes_votes = 42_572_654
no_votes = 43_132_495
percentage = yes_votes / (yes_votes + no_votes)
print(f'[{yes_votes:9}] YES votes [{percentage:2.2%}]')
print('[{:-9}] YES votes [{:2.2%}]'.format(yes_votes, percentage))  # more ugly
print(f'[{percentage}] is approximately [{percentage:.3f}]')

# making columns line up.
print()
table = {'Ken': 1, 'Wu': 234, 'Dcab': 56789}
for name, phone in table.items():
    print(f'[{name:10}]==>[{phone:10d}]')

print()
print('@{0[Wu]:05d}; @{0[Ken]:06}; @{0[Dcab]:d}'.format(table))
print('@{Wu:05d}; @{Ken:06}; @{Dcab:d}'.format(**table))

print('\nThe story of {0}, {1}, and {other}.'.format('Bill', 'Manfred', other='Georg'))
print('abc'.zfill(8))

import sys

# Exception hierarchy: https://docs.python.org/3/library/exceptions.html#exception-hierarchy
# inherit from Exception, as is officially recommended 
# (https://docs.python.org/3/tutorial/errors.html#user-defined-exceptions)

class B(Exception):
    pass

class C(B):
    pass

class D(C):
    pass

print('info of B:', type(B), B.__class__,'@base', B.__base__,
      '\n@dict', D.__dict__,'\n@ctx', D.__context__)

print('\n------  basic except ------')
for cls in [B, C, D, TypeError, NameError, BaseException]:
    try:
        raise cls(1,2,3)
    except (C,D) as e: print("CD", e.__class__, e.args)
    #except B: print("B")
    except Exception as e: print("except:", e.__class__, e.args)
    except BaseException as e: print("baseExcept:", e.__class__, e.args)
    except: # omit the name to serve as a wildcard.
        print("omit name:", sys.exc_info()[:2])
        #raise # re-throw

print('\n------  except with else clause, ------')
try: 100/1
except: print('except')
else: print('else: no exception raised...')
finally: print('finally...')

print('\n------ OldStyleException ------')
class OldStyleException: pass

try:
   raise OldStyleException
#except Exception: print('Exception')          # can catch it
#except BaseException: print('BaseException')  # can catch it
except: print('Omit')                          # can catch it

print('\n------ auto clean up ------')
import os

fn1='f1.txt'
fn2='f2.txt'

def remove(f):
  if os.path.exists(f):
     os.remove(f)

remove(fn1)
remove(fn2)

with open(fn1,'w') as f1, open(fn2,'w') as f2:
  for i,(l1,l2) in enumerate(zip(('aaa','bbb','ccc'),('dd','ee','ff'))):
    f1.write(f'{i}:{l1}+{l2}\n');
    f2.write(f'{i}:{l2}+{l1}\n');

with open(fn1) as f1, open(fn2) as f2:  # the default file mode is 'r'
  for l1,l2 in zip(f1,f2):
    print(l1[:-1],l2[:-1])

print('set operation test')

dict1 = {'a': 3, 'b': 3, 'c':3}
dict2 = {'a': 3, 'b': 5, 'd':3} 
print(dict1,dict2)

x=dict1.items() & dict2.items()
print('intersection',x,' --- ',dict(x))

x = dict1.items() | dict2.items()
print('union ',x,' --- ',dict(x))


x = dict1.items() ^ dict2.items()
print('difference ',x,' --- ',dict(x))

print('-'*9)
share_k = dict1.keys() & dict2.keys()
x = {k: dict1[k] for k in share_k }
print(x)

print('-'*9)
x = {x:dict1[x] for x in dict1 if x in dict2} # best!
print(x)
print('list methods and equivalents', 9*'-')

r = range(10)
lst = [x**2 for x in r]

l1= lst.copy()
l2= lst[:]

l1.append(100)
l2[len(l2):] = [100]

print(l1==l2, '\n', l1, '\n', l2)

l1.clear()
del l2[:]
print(l1==l2, l1, l2)


print('List Comprehensions', 9*'-')

l0=[2*x for x in range(10)]              # 1. more concise and readable
l1=tuple(2*x for x in range(10))         # tuple of #1
l2=list(map(lambda x: 2*x, range(10)))   # 2. equivalently
#l1=(2*x for x in range(10))             # tuple: doesn't work

print('\n',
    'l0==l1?', l0==l1, '\n', 'l0==l2?', l0==l2, '\n',   # list != tuple
    l0, type(l0), '\n',    
    l1, type(l1), '\n',    
    l2, type(l2), '\n',
    [(x, y) for x in l1 for y in l2 if x == y])

mat = [[1,2,3,4],
       [5,6,7,8],
       [9,10,11,12]]

print('flatten', [s for vec in mat for s in vec])
print('transpose', [[vec[i] for vec in mat] for i in range(4)])
print('zip', tuple(zip(*mat))) # zip: make an iterator that aggregates elements from each of the iterables.

print('Tuple', 9*'-')
def print_tval(v): print(type(v),v)

print('empty')
empty=()
print_tval(empty)
empty={}
print_tval(empty)
empty=set()
print_tval(empty)

print('singleton')

singleton = 'str',    # <-- note trailing comma.
print_tval(singleton)

singleton = ('str') # =='str' == string
print_tval(singleton)

t= 1,3,5
x,y,z=t
print(t,x,y,z)

print('letters set', 9*'-')
a = set('alacazam')
b = {x for x in 'azbrxacaxdyaxbyra' if x not in 'xyz'}  # set comprehension
print('element type is', type(next(iter(a))))  # get an any item
print('a=',a,'b=',b)
print('a-b', a-b)   # letters in a but not in b
print('a|b', a|b)   # letters in a or b or both
print('a&b', a&b)   # letters in both a and b
print('a^b', a^b)   # letters in a or b but not both

# Generators (similar to list comprehensions but with parentheses instead of square brackets)

x=sum(i*i for i in range(10))                 # sum of squares
print(x)

xvec = [10, 20, 30]
yvec = [7, 5, 3]
x=sum(x*y for x,y in zip(xvec, yvec))         # dot product
print(x)

from math import pi, sin
sine_table = {x: sin(x*pi/180) for x in range(0, 7)}
print(sine_table)

page=('aa bb cc','gg cc dd', 'dd aa gg')
x = set(word for line in page for word in line.split())
print(type(x), x)

graduates=('ken', 'candy', 'nini', 'ok')
x = ((len(student),student) for student in graduates)
print('@generator=', type(x),x)
x = [*x]  # use * to invoke the generator. It can only be called once!
print('@list=', type(x),x)
x = max(x) # compare len and then name
print('max=', type(x),x)

data = 'golf'
x=list(data[i] for i in range(len(data)-1, -1, -1))
print(data,x)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.2058165548098434, 'fraction_numerical': 0.029713761257385417, 'mean_word_length': 3.732356134636265, 'pattern_counts': {'":': 1, '<': 22, '<?xml version=': 0, '>': 11, 'https://': 5, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '123608', 'license': 'MIT', 'n_tokens_mistral': 6927, 'n_tokens_neox': 6637, 'n_words': 1963, 'path': 'Python.ipynb', 'repo_name': 'cia-exe/python-study', 'size': '17433'}