博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

第五节 Python基础之函数

Posted on 2017-01-25 22:38  Jasonhy  阅读(193)  评论(0编辑  收藏  举报

    函数的定义,有点类似我们之前说的变量一样,也是为了便于维护,同时也提高了代码的可扩展性和重用性。函数分为有返回值的函数和没有返回值的函数(也称过程函数)。示例:

  def function():
      pass
  var = function()
  print(var)
     输出结果是:
        None
  我们定义了函数function(),没有返回值,也就是说返回值数是0,这时我们在调用的时候,默认返回None
 def function():
     return "jasonhy"
 var = function()
 print(var)
    输出结果是:
       jasonhy
 返回的是一个字符串,也就是说返回数是1,我们可以扩展的说,返回的就是Object
 def function():
     return "jasonhy",18
 var = function()
 print(var)
    输出结果是:
       ('jasonhy', 18)
 返回的是一个字符串和一个数字,也就是说返回数大于1,我们可以扩展的说,当返回数大于1的时候,返回的是一个tuple

 

 

   在以上的示例中,我们定义了一个function()函数,这个函数里面是没有参数的,但是有的函数是由参数的,比如list中添加元素的append(self, p_object)函数,p_object就是一个参数,这个参数叫做形参。参数分为形参和实参。

   形参,在函数被调用的时候,才会被分配内存,当这个函数调用结束之后,内存就被释放了,所以我们说形参的作用域是在本函数内。

   实参,也就是在调用函数的时候,将实际的值传给形参,这个实际的值就是实参。

   案例:

  def function(x,y):
      '''
      :param x: 形参
      :param y: 形参
      :return: 返回x + y的和
      '''
      return x+y
  v_sum = function(10,20) #10和20是实参
  print(v_sum)

 

   在进行参数传递的时候,又可以分出两种传递形式,位置参数和关键字参数。位置参数就是我们平时正常进行参数传递形式,也就是要一一对应,多了少了都不可以;那关键字参数就是在进行值传递的时候,直接写上形参等于多少,比如x=1,示例:

  def function(x,y):
      '''
      以关键字参数进行传递示例
      :param x: 形参
      :param y: 形参
      :return: 返回x + y的和
      '''
      return x+y
  v_sum = function(x = 10,y=20) # x = 10,y = 20是关键字参数
  print(v_sum)

    有一点需要注意的就是,当位置参数和关键字参数混合使用的时候,位置参数要写在关键字参数的左边的,也是需要一一对应的。

  我们经常会看到*args, **kwargs这样的参数,比如set集合中的add(self,*args, **kwargs),这样的参数我们叫做参数组。

  案例:

  def function(x,*args):
      '''
      定义一个*args的参数组函数
      :param x: 
      :param args: 参数组
      :return: 返回的也是一个和
      '''
      v_sum = 0
      for item in args:
          v_sum = v_sum + item
      print("输出的x为%d"%x) #测试哪些值传递了x
      print("输出的args为%s"%str(args)) #测试哪些值传给了args
      return x + v_sum
  v_s = function(1,2,3,4,5)
  print(v_s)
     输出结果是:
        输出的x为1
        输出的args为(2, 3, 4, 5)
        15
  从输出的结果,我们发现,第一个实参传递给了x,剩余的以元组的形式传递给args,也就是一种位置参数的形式
  def function(x,**kwargs):
      '''
      定义了一个kwargs参数组的函数
      :param x: 
      :param kwargs: 参数组
      :return: 
      '''
      print(x) #测试x获取的值
      print(kwargs) #测试kwargs获取的值
  function(1,y=2,z=3)
     输出结果是:
        1
        {'y': 2, 'z': 3}
   从输出的结果,我们可以看出,第一个参数传递给了x,也就是位置参数,剩下的两个参数以字典的形式传递给了kwargs,所以说kwargs是关键字参数

 

     全局变量和局部变量:

   全局变量是定义在函数外面的值,作用域是在本程序中都有效,局部变量是定义在函数里面的值,作用域是在本函数中,一般情况我们都是全局变量用大写,局部变量用小写,比如:

  A = 1
  B = 2

  def add():
      '''
      add()函数里定义了两个变量
      :return:
      '''
      a = 1
      b = 2
      return a + b

  def sub():
      '''
      sub()直接返回两个全局变量的差值
      :return:
      '''
      return B - A

  print(add())
  print(sub())

     嵌套函数: 就是在函数内定义函数,而函数的作用域在定义的时候就已经固定了,不会随着调用位置的改变而改变

    USER_NAME = "Jasonhy"

    def get_name():
        '''
        定义了一个全局变量,然后将全局变量赋值给局部变量
        :return: 将内层函数名返回
        '''
        name = USER_NAME
        def print_name():
            print("print_name()函数:",name)
        return print_name
    name = get_name()  # 返回的是内层函数的地址,我们通过地址就可以直接调用内层函数
    print(name)     # 输出的结果是:<function get_name.    <locals>.print_name at 0x0065E858>
    name()      # 输出结果是:print_name()函数: Jasonhy

    递归: 就是函数内部自己调用自己,具有以下特性:

       ①必须有一个明确的结束条件,如果没有明确的结束条件的话,整个函数将会进入一个不断的递归调用中,进入了一个死循环

       ②每

递归一次,问题规模都会减少,向结束条件靠拢

       ③递归的效率不高,因为要重复的调用,知道条件满足,很耗性能,如果递归的次数过多,还容易造成栈溢出

    案例:

  LIST_NUM = [11,33,44,45,66,78,88]

  def find_num(list,num):
      print(list)
      if len(list) > 1:
          mid = int(len(list) / 2)  # 如果列表的长度大于1,将列表的长度一般len赋给中间变量mid
          if list[mid] == num:
              print("找到数字了",list[mid])
          elif list[mid] > num:   # 说明找到的数字在左边
              print("\033[31;1m找到的数在%s左边\033[0m" % list[mid])
              return find_num(list[0:mid],num)    # 从左边开始找
          else:  # 找到的数在mid的右边
              print("\033[31;1m找到的数在%s右边\033[0m" % list[mid])
              return find_num(list[mid+1:],num)
      else:
          if list[0] == num:
              print("找到数字了",list[0])
          else:
              print("要找的数字不在列表中!!!")
  find_num(LIST_NUM,44)

     匿名函数: 就是没有函数名的函数,定义的格式如下:

          lambda 参数 : 返回值

          匿名函数一般是和其他函数结合使用

     案例:

  # 找出最大值,一般情况我们通过for的来遍历value值,这里我们可以通过匿名函数来完成
  AGE_DICT = {"a_age":18,"b_age":25,"c_age":20}
  max_age = max(AGE_DICT,key = lambda k : AGE_DICT[k])
  print(max_age,AGE_DICT[max_age])

    函数式编程: 在语言编程中,有面向过程,函数式,和面向对象三种,面向过程简单的说,就是做一件事需要自己一步步去安排,而面向对象就是自己不需要知道过程是怎么进行的,需要的时候直接去调用就行了,函数式呢?就是我们定义的编程函数加上数学意义的函数,它的特性如下:

    ①不可变,不用变量保存状态,不能修改变量

    # 非函数式
    NUM = 1
    def non_functional():
        global NUM
        NUM += 1
        return NUM
    non_functional()
    print("非函数式",NUM)
    # 函数式
    def functional():
        return NUM + 1
    functional()
    print("函数式",NUM)

     ②第一类对象,函数即"变量"

        高阶函数:函数接收的参数是一个函数名;返回值包含函数名

     ③尾调用,也就是在函数的最后一步调用另一个函数,我们需要注意的是,最后一行不一定是最后一步,需要补充的是,关于尾调用的优化就是在最后一步的时候进行递归

  def set_name(name):
      print("通过get_name函数被调用了")
      return name
  def get_name(name):
      '''
      如果在这个函数中,给set_name()赋一个变量x后,再将x返回,那么就不是尾调用了,
      因为set_name()不是在最后一步调用;另外如果返回时set_name() + "= 18",也不是
      尾调用了,因为也不是最后一步调用了set_name(),先进行+运算,然后再将值返回
      :param name: 
      :return: 
      '''
      return set_name(name)
  get_name("Jasonhy")

  def set_sex(sex):
      print("通过get_msg函数调用了set_sex")
    return sex
  def set_age(age):
      print("通过get_msg函数调用了set_age")
      return age

  def get_msg(type_msg):
      '''
      在if和elif中,哪个都有可能成为最后一步,但是在这个函数中的最后一行是elif下面的
      返回值
      :param type_msg:
      :return:
      '''
      if type(type_msg) == str:
          return set_sex(type_msg)
      elif type(type_msg) == int:
          return set_age(type_msg)
  get_msg(18)

       map()函数:主要是用来修改列表的元素,而且每个元素的顺序不改变,在map函数中,第一个参数是处理方法,一般情况我们可以直接用匿名函数来完成,第二个参数是可迭代对象,得到的结果是一个可迭代对象

  LIST_NUM = [1,2,3,4,5]
  print(list(map((lambda x : x + 1),LIST_NUM)))   # 输出结果是:[2, 3, 4, 5, 6]

      filter()函数:主要用于遍历序列中的每个元素,判断每个元素的布尔值,如果是True则留下

  LIST_STR = ["ours","yours","theirs","we","you","they"]
  print(list(filter((lambda x : not x.endswith("rs")),LIST_STR)))     # 需要的没有rs的  输出结果是:['we', 'you', 'they']

      reduce()函数:主要用来对序列的合并

  from functools import reduce
  LIST_NUM = [1,2,3,4,5]
  print(reduce((lambda x,y:x + y),LIST_NUM))  # 输出结果是15

     内置函数:

    案例一:数学运算

  print(abs(-2))  # 求绝对值  输出结果是: 2
  print(complex(12,3))    # 创建一个复数,第一个参数是实数部分,第二个参数是虚数部分  输出结果是: 12 + 3j
  print(divmod(10,3))     # 取商和模  返回的是元组类型的,第一个元素是商,第二个元素是模   输出结果是:(3,1)
  print(float("1"))       # 将一个数字字符串或者数字变成浮点数     输出结果是:1.0
  print(int(2.3))         # 将一个字符串数字或者数字变成整型       输出结果是:2
  print(pow(2,3,3))       # 有两种类型,如果只有两个参数x和y,则返回的结果是x的y次幂,如果有三个参数x,y和z,则返回的结果是x的y次幂,然后对z取模   输出结果是:2
  print(list(range(5)))   # 返回一个序列,默认从0开始     输出结果是:[0, 1, 2, 3, 4]
  print(round(4.6456878412,3))    # 四舍五入 第二个参数表示保留几位小数    输出结果是:4.646
  print(sum([1,2,3,4,5]))     # 对一个序列进行求和     输出结果是:15
  print(oct(10))      # 将十进制变成八进制     输出结果是:0o12
  print(hex(10))      # 将十进制变成十六进制    输出结果是:0xa
  print(bin(10))      # 将十进制变成二进制     输出结果是:0b1010
  print(chr(65))      # 返回一个整数对应的ASCII字符      输出结果是:A

 

    案例二:集合类操作

  # 格式化输出字符串,格式化的参数顺序从0开始
  # 在讲内置format()函数之前,我们先来看str的format()方法
  # 通过位置来映射
  print("{0},{1}".format("Jasonhy",18))
  print("{},{}".format("Jasonhy",18))
  print("{0},{1},{0}".format("Jaosnhy",18))   # 输出结果是:Jaosnhy,18,Jaosnhy
  # 通过关键字参数
  print("{name},{age}".format(name = "Jasonhy",age = 18))
  # 通过对象属性
  class Person:
      def __init__(self,name,age):
          self.name,self.age=name,age
      def msg(self):
          return "My name is {self.name},is {self.age} old".format(self=self)
  p = Person("Jasonhy",18)
  print(p.msg())      # 输出结果是:My name is Jasonhy,is 18 old
  # 通过下标
  p = ["Jasonhy",18]
  print("{0[0]},{0[1]}".format(p))
  # 填充与对齐
  # ^ --居中, < -- 左对齐, > -- 右对齐 :号后面带填充的字符,只能是一个字符,不指定的话默认用空格填充
  print("{:>8}".format("187"))
  print("{:0>8}".format("187"))
  print("{:a^8}".format("187"))       # 输出结果是:aa187aaa
  # 精度与类型f
  # 表示留两位小数
  print("{:.2f}".format(3.1415923))
  # 进制 b,d,o,x分别表示二进制,十进制,八进制,十六进制
  print("{:b}".format(10))
  print("{:o}".format(10))
  print("{:x}".format(10))
  # 可以用逗号来做金额千位隔开
  print("{:,}".format(12545112131))   # 输出结果是:12,545,112,131

  # format()内置函数
  f = format(1236)
  print(type(f),f)    # 将数字格式成字符串     输出结果是:<class 'str'> 1236
  print(format(10,"X"))   # 将十进制变成十六进制    输出结果是:A
  print(format(3.14,"0=10"))      # 左边填充  输出结果是:0000003.14
  print(format(3.1415923,"05.2"))     # 有效位是2位,一共有5位,不够在前面补0      输出结果是:003.1
  print(format("Jasonhy","*<10"))     # 左对齐10,不够以*填充,默认是空格        输出结果是:Jasonhy***
  e = enumerate("Jasonhy")    # 将一个序列变成索引序列 如果调用next()方法,返回的是一个元组,第一个元素是索引,第二个元素是对应的值
  for index,value in e:
      print(index,value,end=",")      # 输出结果是:0 J,1 a,2 s,3 o,4 n,5 h,6 y,

  i = iter("Jasonhy")     # 生成一个迭代器
  print(i.__next__())     # 输出结果是:J

  print(max("456123"))   # 返回序列中的最大值
  print(min("456123"))   # 返回序列中的最小值

  print(dict(name="Jasonhy",age="18"))    # 以键对的方式创建一个字典      输出结果是:{'name': 'Jasonhy', 'age': '18'}
  print(dict(zip(["name","age"],["Jasonhy",18])))     # 以映射函数的方式来构建字典
  print(dict([("name","Jasonhy"),("age",18)]))        # 以可迭代对象方式来构建字典

  print(sorted([4,5,1,3,2,6,8]))      # 排序

 

    实例三:逻辑判断

  l = ["12","",None,465]
  print(all(l))   # 参数是一个序列,然后遍历序列中的每个元素,如果每个元素都为真,则返回真,否则返回False     输出结果是:False
  print(any(l))   # 与all相反,只要有一个为True,那么返回就是True      输出结果是:True

     实例四:反射

# callable() 检查对象object是否可以调用,如果返回True,object仍有可能调用失败,但是如果返回False,调用object一定不会成功
print(callable(str))        # 输出结果是True
print(callable("str"))      # 输出结果是False
print(callable(list))       # 输出结果是True
print(callable([1,2,3]))    # 输出结果是False
# 如果类中声明了__call__()方法,那就可以调用了
class People:
    def __call__(self, *args, **kwargs):
        return 0
p = People()
print(callable(p))      # 输出结果是:True
# classmethod 注解,用来说明这个方法是类方法,类似Java中的static,可以被类调用,也可以被类的实例调用,类方法不需要self参数
class Animal:
    @classmethod
    def name(cls,*args,**kwargs):
        return 0
a = Animal()
print(a.name())
print(Animal.name())

# compile()将source编译为代码或者AST对象,代码对象能够通过exec语句来执行或者eval进行求值
# filename:代码文件名称,如果不是从文件读取代码则传递一些课辨认的值
# model:指定编译代码种类,可以自定"exec","eval","single",如果是exec类型,表示一个序列语句,可以进行运行,如果是一个eval,表示是一个单一的表达式语句,可以用来计算相应的值出来,如果是single,表示是一个单一的语句,采用交互模式执行,在这种情况下,如果是一个表示式,一般输出为结果,而不会是以None输出
v_str = "for item in range(5):print(item,end=',')"
c = compile(v_str,"","exec")   # 编译字节码对象
exec(c)     # 执行        输出结果是:0,1,2,3,4,
v_str = "1*2 + 2*3"
c = compile(v_str,"","eval")
print(eval(c))
# 列出某个类型的可用方法
print(dir(str))     # 输出结果是:['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
class People:
    def __init__(self):
        self.name = "Jasonhy"
        self.age = 18
    def printAttr(self):
        print("属性有name[%s]和age[%s]"%(self.name,self.age))
p = People()
p.printAttr()
# 获取属性值
print(getattr(p,"name","not find"))     # 如果有这个属性,获取属性的值 否则是not find
# 判断属性值是否存在
print(hasattr(p,"name"))
# 给属性赋值
setattr(p,"age",20)
print(getattr(p,"age","not find"))
# 删除属性
delattr(p,"age")
print(hasattr(p,"age"))     # 输出结果是False
# 如果对象为哈希表类型,返回一个哈希值
print(hash(p))
# 返回对象的唯一标识
print(id(p))
#
# 逻辑判断object是否是class实例
print(isinstance(p,People))     # 输出结果是:True

class Man(People):
    def __str__(self):
        return 0
    def printRes(self):
        print('我是People的子类')
print(issubclass(Man,People))       # 输出结果是True

# 返回内存镜像类型的对象
v = memoryview(b'abcd')
print(list(v))      # 输出结果是:[97, 98, 99, 100]
# 属性访问的包装类
class Test:
    def __init__(self):
        self._test = None
    def getTest(self):
        return self._test
    def setTest(self,value):
        self._test = value
    tee = property(getTest,setTest)
test = Test()
test.tee = "测试用例"
print(test.tee)

print(repr("Jasonhy\t"))    # 将一个对象变成可打印格式   输出结果是:'Jasonhy\t'

class TestStatic:
    def __init__(self):
        self._test = "test"
    # 声明一个方法为静态方法,可以通过类名直接调用
    @staticmethod
    def printRes():
        print("声明静态方法")
TestStatic.printRes()

# 返回对象变量
print(vars(People))
# bytearray 返回一个byte数组
# 有三个参数,source,encoding,error
# 如果source为字符串,encoding必须提供
# 如果source为整数,返回这个整数所指定的长度的空字节数组,整数必须大于0
# 如果source参数实现了buffer接口的object对象,那么将使用只读的方式将字节读到字节数组后返回
# 如果source参数一个可迭代对象,那么这个迭代对象的元素都必须符合0 <= x < 256以便初始化到数组中
b = bytearray("Jasonhy",encoding="utf")        # 报错TypeError: string argument without an encoding
print(b)
b = bytearray(1)
print(b)        # 输出结果是: bytearray(b'\x00')
b = bytearray([1,2,3])
print(b)
b = bytearray([456,2])      # 报错: ValueError: byte must be in range(0, 256)
print(b)

# zip 合并列表,将对象中对应的元素合并成一个元组,然后返回由这些元组组成的列表,若传入的参数不等,则返回长度相同对象最短的,可以利用*来解压list
a = [1,2]
b = [3,4]
z = zip(a,b)
l = list(z)
print(l)        # 输出结果是:[(1, 3), (2, 4)]
z = zip(*l)
print(list(z))  # 输出结果是:[(1, 2), (3, 4)]