# -*- coding:utf-8 -*-
# ------函数(参数)-------------
'''函数是组织好的,可以重复使用的,用来实现单一,或相关联功能的代码
定义函数用def语句,一次写出函数名,括号,括号中的参数和冒号,然后在缩进块中编写函数体,
函数的返回用return返回
函数总有返回值,就算没有return也会返回none
支持递归调用,但不能进行尾递归优化
一个完整的函数对象由函数和代码两部分组成。其中,PyCodeObject 包含了字节码等执⾏行数据,
⽽而 PyFunctionObject 则为其提供了状态信息。
'''
#abs调用
print(123); #123,传的参数类型和数量必须正确,否则报错

#比较函数cmp(x,y) xy return 1,x=y return 1;
print cmp(4,5); #-1
print cmp(5,4); #1
print cmp(5,5); #0

#数据类型转换,如int()函数可以将其他数据类型转换为整数
a='12';
print type(a); #
print int(a);  #12
print type(int(a)); #
print float(a); #12.0
print type(float(a)); #
print bool(0); #False

#函数名就是指向一个函数对象的引用,可以把函数名赋值给一个变量,相当于给函数起别名
s = cmp #给cmp函数起别名s,s指向cmp函数
print s(5,4);  #1 通过别名调用函数

#-------------------------------------
def sayHello():    #定义函数sayhello
    print 'Hello world!'  #函数块
sayHello();   #Hello world!  调用


#-----函数参数--------
def output(x):
    print (x);
    if x >0:
        return x;
    else:
        return -x;
output(-9);  #-9
output('a'); #a

#--参数检查--参数个数不对,python解释器会报错,但是类型不对不会报错(对于自定义函数)
#内置函数abs()如果传入字符串类型会检查出来,而我们上例中定义的output()则无法检查
# def outputs(x):
#     print (x);
#     if not isinstance(x,(int,float)):
#         raise TypeError('you have input a bad object');
#     if x >0:
#         return x;
#     else:
#         return -x;
# outputs('b');
# 这里会应影响下面代码的执行,所以我把这一块全部注掉

'''
File "D:/pycharmhome/venv/demo5.12.27.py", line 51, in 
  File "D:/pycharmhome/venv/demo5.12.27.py", line 46, in outputs
  raise TypeError('you have input a bad object');
TypeError: you have input a bad object
'''
#------
def printMax(a,b):
    if a>b:
        print a,'is the bigger one';
    else:
        print b,'is bigger one';
printMax(3,4); #4 is bigger one
x=1;
y=2;
printMax(x,y); #2 is bigger one
printMax('c','b') #c is the bigger one 可以用参数检查只数int类型

#------空函数---------
#pass 占位符,用来标记空代码块。还没有想好怎么写函数,可以先放一个pass不会报错
def text():
    pass;
for i in xrange(1,10):
    pass;

#------函数返回多个值--
import math
def move(x,y,step,angle=0):
    nx = x + step * math.cos(angle);
    ny = y - step * math.sin(angle);
    return nx,ny;
x,y = move(100,100,23,30);
print x,y;   #103.547783347 122.724727354
'''
这是一个假象,函数返回的仍是一个值,返回值是一个tuple,在语法上tuple可以省略括号,而
多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,python的函数返回多值其实就是
返回一个tuple,但写起来方便
'''



#-----------------------------------------------------------------------
#函数的参数 function parameters---
#上面例子中59行printmax函数就进行了传参
def power(i):
    return i*i;
print power(5) #25
#-------
def power(i,n):
    s = 1;
    while n>0:
        n = n-1; #n控制循环次数,1*i*i*i..控制这里i出现几次
        s = s*i;
    return s
print power(5,2); #25 这是power(5)会报错

#----设置默认参数
def power(i,n=2):
    s = 1;
    while n > 0:
        n = n-1;
        s=s*i;
    return s;
print power(5); #25
print power(2,3); #8 当n>2时明确传入n

#-定义时,必选参数在前默认参数在后。否则报错
#函数传参方式灵活多变,可按照位置顺序传,也可以不关心位置命名来传
def test(a,b):
    print a,b;
test(1,'a'); #1 a
test(b = 'a', a=1); #1 a

#-默认参数的好处,简化书写,不用在调用的时候再输入值
def stu(name,gender,age = 3,city = '西安'):
    print 'name:',name;
    print 'gender:',gender;
    print 'age:',age;
    print 'city:',city;
stu('二大爷','man');
'''
name: 二大爷
gender: man
age: 3
city: 西安
'''
'''
注意:默认值参数必须指向不变的对象,否则在每次调用的时候他都会记住上次调用的值,当新
调用时会将之前的值和现在传入的值同时输出。下面看一个错误的例子
'''
def box(i,intlist = []):
    intlist.append(i);
    return intlist;
print box(4); #[4]
print box(3); #[4, 3]
print box(5,[]);#[5] 显示的提供实参不适用默认值是正确的
print box(5); #[4, 3, 5]再次使用默认值
print box(5); #[4, 3, 5, 5]再再次使用默认值
'''
因为这里我用的是list作为默认值,而list的长度可变,当每次调用的时候都会给里面添加一个
数字i=?,则在下一次调用的时候list这个表里都会有上次所添加的数字,来作为这个box函数中
的默认参数。即第2次 intlist=[4],第3次 intlist=[4,3]...
而如果显示的给出实参不提供默认值,相当于格式化这个表 即5=intlist,输出的永远只有一个数字
'''
#所以通常不用可变对象来作为默认值
#可以用不变的None来修改上面的例子
def boxs(i,intlist = None):
    if intlist is None:
        intlist = [];
    intlist.append(i);
    return intlist;
print boxs(3); #[3]
print boxs(4); #[4]
#-------------------------------------------------------
#--可变参数--
def num(numbers):
    sum = 0;
    for n in numbers:
        sum = sum + n*n   #
    return sum;
print num([1,2,3]);#14 将参数组装成一个list sum=1*1+2*2+3*3
print num((2,3)); #13 将参数组装成一个tuple sum=2*2+3*3

#------用可变参数简化调用----------------------------------------
def nums(*numbers):
    sum = 0;
    for n in numbers:
        sum = sum + n*n
    return sum;
print nums(2,4); #20
print nums(); #0 可以传0个参数
#--已经有一个tuple或list,调用可变参数
num = [2,2];
print nums(num[0],num[1]);#8 这样比较麻烦
print nums(*num); #8 允许在list或者tuple前加*,把list或者tuple的元素变为可变参数传进去

#-----------------------------------------------------
#--关键字参数---
# 可变参数允许传入0或人一个参数,这些可变参数在函数调用是自动组装为一个tuple。而关键字
# 参数允许传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict

def person(name,age,**kwargs):
    print 'name:',name,'age:',age,kwargs;
print person('二大爷','77',qq = 123,email = '[email protected]');
# name: 二大爷 age: 77 {'qq': 123, 'email': '[email protected]'}
#也可以先组装一个dict,然后,把dict转换为关键字传进去

kw = {'city':'xianyang',
          'job':'teacher',
          'qq':123}
print person('二大爷',77,city = kw['city'],job=kw['job']);
# name: 二大爷 age: 77 {'city': 'xianyang', 'job': 'teacher'}

print person('二大爷',77,**kw);
# name: 二大爷 age: 77 {'qq': 123, 'city': 'xianyang', 'job': 'teacher'}

# -------参数组合--------------
def allbox(a,b,c=0,*args,**kwargs):
    print 'a:',a,'b:',b; #必选参数
    print 'c:',c; #默认参数
    print 'args:',args; #可变参数
    print 'kwargs:',kwargs #关键字参数
print allbox(1,2);
'''
a: 1 b: 2
c: 0
args: ()
kwargs: {}
None
'''
print allbox(1,2,3,'a','b','c',d = 4,e = 5);
'''
a: 1 b: 2
c: 3
args: ('a', 'b', 'c')
kwargs: {'e': 5, 'd': 4}
'''
#也可以通过tuple和dict调用
args = (1,2,3,4,5); #list也行
kwargs = {'e':4};
print allbox(*args,**kwargs);
'''
a: 1 b: 2
c: 3
args: (4, 5)
kwargs: {'e': 4}
'''

# -------补充-----------
# 变参智能功能放在所有定义参数的尾部,切**kwargs必须是最后一个。
def tes(*arg,**kwargs):    #可接收任意参数的函数
    print arg;
    print kwargs;
print tes(1,2,'a','b',c='c',d='d'); #位置(必选)、命名(关键字)
'''
(1, 2, 'a', 'b')
{'c': 'c', 'd': 'd'}
'''
print tes('a'); #只传位置参数
'''
('a',)
{}
'''
print tes(a='a');#只传命名(关键字)参数
'''
()
{'a': 'a'}
'''

#-----------可以展开序列类型和字典,将全部元素当多个实参使用。不展开仅是单个实参对象
def zk(a,b,*args,**kwargs):
    print a,b;
    print args;
    print kwargs;
print zk(*xrange(1,7));
'''
1 2
(3, 4, 5, 6)
{}
'''
#单个*用来展开序列类型,或者仅是字典的主键列表。**用来展开字典的值,如果没有变参收集
# 展开后多余的参数会引发异常.如上例把*args去掉就有异常;
def zk_dic(a,b):
    print a;
    print b;
dic = {'a':4,'b':5};
print zk_dic(*dic);
'''
a
b
'''
print zk_dic(**dic);
'''
4
5
'''