目录

1. 函数的定义

python中函数有两种:

  • python自带的函数
  • 用户定义函数

返回多个值

原来返回值是一个tuple!但是,在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便

1.1函数的参数

参数 含义 输入
位置参数 def power(x,n) 实际参数
默认参数 def power(x,n=2) 实际+默认参数(需要改变时)
可变参数 def power(*args) 传入任意个参数,内部组装成tuple
关键字参数 def person(name, age, **kw) 传入带参数名的参数,组装成dict
命名关键字参数 def person(name,age,*, city, job) 限制关键字参数的名字(必须传入参数名)
  • 顺序: 必选参数<---默认参数<---可变参数<---命名关键字参数<---关键字参数
  1. # 关键字参数
  2. def person(name, age, **kw):
  3. print('name:', name, 'age:', age, 'other:', kw)
  4. person('hao', 20) # name: Michael age: 30 other: {}
  5. person('hao', 20, gener = 'M', job = 'Engineer') # name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
  6. extra = {'city': 'Beijing', 'job': 'Engineer'}
  7. person('Jack', 24, **extra)
  8. # 命名关键字参数
  9. def person(name, age, *, city='Beijing', job):
  10. print(name, age, city, job)
  11. person('Jack', 24, job = '123')
  12. person('Jack', 24, city = 'Beijing', job = 'Engineer')
  13. # Combination
  14. # 可变 + 关键字参数
  15. def f1(a, b, c=0, *args, **kw):
  16. print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
  17. f1(1, 2, 3, 'a', 'b') # a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
  18. f1(1, 2, 3, 'a', 'b', x=99) # a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}
  19. # 默认参数 + 命名关键字参数 + 关键字参数
  20. def f2(a, b, c=0, *, d, **kw):
  21. print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
  22. f2(1, 2, d=99, ext=None) # a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}

2. 面向对象编程

  • 面向过程: 根据业务逻辑从上到下写代码
  • 面向对象: 对数据与函数绑定到一起,进行封装,这样更快速的开发过程,减少代码重复使用

数据封装、继承和多态是面向对象的三大特点

2.1. 类(抽象概念)和对象(具体概念)

玩具模具(类)-》 火车玩具,飞机玩具..(对象)

类的组成结构

  • 类名:狗
  • 类的属性:一组数据(狗的颜色,性别...)
  • 类的方法: 运行进行操作的方法行为(行为,会叫,会咬人...)-> 用函数设计
类的组成 特性 例子 例子
类名 名称
类的属性 一组数据 狗的颜色,性别 身高,年龄
类的方法 运行进行操作的方法行为 行为,会叫,会咬人 跑,打架

  • 类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;
  • 方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据;
  • 通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。
  • 继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。

(1). 定义类

  1. # 定义类
  2. class Dog(object):
  3. # 定义初始化方法
  4. def __init__(self,weight,color):
  5. """
  6. self: python会把对象的地址自动传给self,不要自己传
  7. weight, color: 接收外部属性
  8. """
  9. # 定义属性
  10. self.weight = weight
  11. self.color = color
  12. # 魔法方法: 当只打印Dog对象的时候,就可以打印这里的东西
  13. def __str__(self):
  14. msg = "dog weight" + self.weight + "color" + self.color
  15. return "哈哈哈"
  16. def getweight(self):
  17. return self.weight
  18. def getcolor(self):
  19. return self.color
  20. def setweight(self):
  21. self.weight = 100
  22. def setcolor(self):
  23. self.color = "green"
  24. # 定义方法
  25. def bark(self):
  26. """
  27. self: python解释器默认把调用该class的对象地址给它
  28. """
  29. print("666")
  30. def run(self):
  31. print("777")
  32. # 创建对象
  33. huskey = Dog(5, 'Black') # 创建一个哈士奇
  34. keji = Dog(10, 'Green')
  35. huskey.bark() # 哈士奇叫
  36. huskey.run() # 哈士奇跑
  37. huskey.weight = 100 # 哈士奇属性
  38. huskey.color = 'green'
  • self表示自己,表示调用类的对象本身
  • python中类似 __***__的方法,是魔法方法,有特殊用途

(2). 类的数据封装

面向对象编程的一个重要特点就是数据封装,比如在上面例子中,Dog类的每个实例都有weight和color,我们可以直接在Dog类的内部定义访问数据的函数,这样,就把“数据”给封装起来了。这些封装数据的函数是和Dog类本身是关联起来的,我们称之为类的方法:

  1. class Dog(object):
  2. def __init__(self, weight, color):
  3. self.weight = weight
  4. self.color = color
  5. def print_dog(self):
  6. print('%s: %s' % (self.weight, self.color))

我们从外部看Dog类,就只需要知道,创建实例需要给出weightcolor,而如何打印,都是在Dog类的内部定义的,这些数据和逻辑被“封装”起来了,调用很容易,但却不用知道内部实现的细节。

(3). 访问限制

  • 从前面Dog类的定义来看,外部代码还是可以自由地修改一个实例的weightcolor属性,我们可以设置内部属性不被外部访问:

  1. class Dog(object):
  2. # 加下划线
  3. def __init__(self, weight, color):
  4. self.__weight = weight
  5. self.__color = color
  6. def print_dog(self):
  7. print('%s: %s' % (self.__weight, __self.color))
  8. huskey = Dog(60, 'green')
  9. print(huskey.__name) # 无法再访问属性,会出错
  • 但是如果外部代码需要获取weightcolor属性呢?-> 添加get_weightget_color方法
  1. class Dog(object):
  2. ...
  3. def get_weight(self):
  4. return self.__weight
  5. def get_color(self):
  6. return self.__color
  • 如果又要允许外部代码修改weightcolor怎么办?->添加set_weightset_color方法
  1. class Dog(object):
  2. ...
  3. def set_weight(self,weight):
  4. self.__weight = weight
  5. def set_color(self):
  6. self.__color = color
  • 为什么要费这么大周折呢?之前不是可以直接husky.weight=10直接改吗

    因为在方法中,可以对参数做检查,避免传入无效的参数:
  1. class Dog(object):
  2. ...
  3. def set_weight(self,weight):
  4. if 0<=weight<=100:
  5. self.__weight = weight
  6. else:
  7. raise ValueError('Bad weight')

(4). 获取对象信息

  1. # type():判断对象类型
  2. type([1,2,3])
  3. type('abc') == str
  4. type(huskey)
  5. # isinstance(): 一个对象是否属于某种类型
  6. isinstance(h, husky)
  7. isinstance(h, cat)
  8. # dir() 获取一个对象的所有属性和方法
  9. dir('ABC')
  10. dir('huskey')

2.2. 继承和多态

继承就是:父类和子类之间的交互关系

(1)为什么要用继承?

最大的好处是子类获得了父类的全部功能

  1. class Animal(object):
  2. def run(self):
  3. print('Animal is running...')
  4. # Dog继承父类Animal,并且自动拥有run属性,可以在Dog对象中直接调用
  5. class Dog(Animal):
  6. pass
  7. # 子类可以自己增加一些方法(eat),同时也可以重写父类的方法(run)---》这就是多态
  8. class Cat(Animal):
  9. def run(self):
  10. print('Dog is running...')
  11. def eat(self):
  12. print('Eating meat...')

(2)什么是多态?

在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是反过来就不行了

C是dog,C也是animal

D是animal,但你不能说D也是Dog

  1. # 首先创建对象并查看对象类型
  2. a = list() # a是list类型
  3. isinstance(a, list) # true
  4. b = Animal() # b是Animal类型
  5. isinstance(b, Animal) # true
  6. c = Dog() # c是Dog类型
  7. isinstance(c, Dog) # true
  8. # C既是Dog也是Animal
  9. isinstance(c, Animal) # true
  10. # 反过来就错了
  11. isinstance(b, Dog) # false

(3)那么为题来了。这样设置成多态有什么好处呢?

  • 任何依赖父类作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。

  • 当我们需要传入DogCatTortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态的意思

  • 对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定

  1. def run_twice(animal):
  2. animal.run()
  3. run_twice(Animal()) # 调用animal的run
  4. run_twice(Dog()) # 调用dog的run
  5. run_twice(Cat()) # 调用cat的run

3. 面向对象高级编程


(1). __slots__的使用

动态绑定允许我们在程序运行的过程中动态给class加上功能, 但是,如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加nameage属性。

class中定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

  1. class Student(object):
  2. __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
  3. s = Student() # 创建新的实例
  4. s.name = 'Michael' # 绑定属性'name'
  5. s.score = 99 # 绑定属性'score'-->出错

__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的

(2). @property的使用

在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改,解决方案:

  1. 通过set_score设置成绩,在通过get_score获取成绩,在set_score中检查参数(已经讲过)
  2. 使用内置的@property装饰器,既可以检查参数,又可以类似属性那样访问类的变量

@property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

  1. class Student(object):
  2. # @property将get_Score方法变成属性
  3. @property
  4. def score(self):
  5. return self._score
  6. # @score.setter将set_score方法变成属性赋值
  7. @score.setter
  8. def score(self, value):
  9. if not isinstance(value, int):
  10. raise ValueError('score must be an integer!')
  11. if value < 0 or value > 100:
  12. raise ValueError('score must between 0 ~ 100!')
  13. self._score = value
  14. s = Student()
  15. s.score = 60 # OK,实际转化为s.set_score(60)
  16. s.score # OK,实际转化为s.get_score()
  17. s.score = 9999 # error

(3). 多重继承MixLn的设计

MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系

  1. class Animal(object):
  2. pass
  3. class Bird(Animal):
  4. pass
  5. class Parrot(Bird):
  6. pass
  7. # 此时我们需要加入额外的功能fly
  8. # 先定义好fly的类
  9. class Flyable(object):
  10. def fly(self):
  11. print('Flying...')
  12. # 同时继承两个类
  13. class Parrot(Bird, Flyable):
  14. pass

(4). 定制个性化类

1): __str____repr__

让打印的object更漂亮:

  • __str__: 用print打印
  • __repr__: 直接输入对象名
  1. class Student(object):
  2. # 加下划线
  3. def __init__(self, name):
  4. self.__name = name
  5. def __str__(self):
  6. msg = 'Student name is' + self.__name
  7. return msg
  8. __repr__ = __str__
  9. s = Student()
  10. print(s) # 打印出 Student name is.....
  11. s # 效果跟上面的一样

2): __iter__

将类定义成类似list或者tuple那种,可以用于for循环的作用

  1. class Fib(object):
  2. def __init__(self):
  3. self.a, self.b = 0, 1 # 初始化两个计数器a,b
  4. def __iter__(self):
  5. return self # 实例本身就是迭代对象,故返回自己
  6. def __getitem__(self, n):
  7. if isinstance(n, int): # n是索引
  8. a, b = 1, 1
  9. for x in range(n):
  10. a, b = b, a + b
  11. return a
  12. if isinstance(n, slice): # n是切片
  13. start = n.start
  14. stop = n.stop
  15. if start is None:
  16. start = 0
  17. a, b = 1, 1
  18. L = []
  19. for x in range(stop):
  20. if x >= start:
  21. L.append(a)
  22. a, b = b, a + b
  23. return L
  24. def __next__(self):
  25. self.a, self.b = self.b, self.a + self.b # 计算下一个值
  26. if self.a > 100000: # 退出循环的条件
  27. raise StopIteration()
  28. return self.a # 返回下一个值
  29. for i in Fib():
  30. print(n) # 1,1,2,3,5,.......,75025
  31. # 因为__getitem__的作用,可以index某个值
  32. f = Fib()
  33. f[0] # 1
  34. # 也可以切片
  35. print(f[0:5]) # 1,1,2,3,5

3): __call__

直接在实例本身上调用的一种构造方法

  1. class Student(object):
  2. def __init__(self, name):
  3. self.name = name
  4. def __call__(self):
  5. print('My name is %s.' % self.name)
  6. s = Student('Michael')
  7. s() # My name is Michael.
  • 实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象

4) 枚举类Enum

定义常量

  1. from enum import Enum, unique
  2. # unique保证没有重复值
  3. @unique
  4. class Weekday(Enum):
  5. Sun = 0 # Sun的value被设定为0
  6. Mon = 1
  7. Tue = 2
  8. Wed = 3
  9. Thu = 4
  10. Fri = 5
  11. Sat = 6
  12. # 访问枚举类
  13. print(Weekday.Mon) # Weekday.Mon
  14. print(Weekday.Mon.value) # 1
  15. print(Weekday(1))) # # Weekday.Mon

5) type()动态创建类Class

type()函数既可以返回一个对象的类型,又可以创建出新的类型

type()和Class()的功能是一样的

要创建一个class对象,type()函数依次传入3个参数:

  • class的名称;
  • 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
  • class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
  1. def fn(self, name='world'): # 先定义函数
  2. print("hello", name)
  3. Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class

4. 错误处理

(4.1).try-catch->有错误后就结束了不执行以后的

  1. try:
  2. print('try...')
  3. r = 10 / int('a')
  4. print('result:', r)
  5. except ValueError as e:
  6. print('ValueError:', e)
  7. except ZeroDivisionError as e:
  8. print('ZeroDivisionError:', e)
  9. finally:
  10. print('finally...')
  11. print('END')
  12. try...
  13. except: division by string
  14. finally...
  15. END

try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except语句块,执行完except后,如果有finally语句块,则执行finally语句块,至此,执行完毕。

(4.2).logging->有错误了打印完信息后继续执行

  1. import logging
  2. def foo(s):
  3. return 10 / int(s)
  4. def bar(s):
  5. return foo(s) * 2
  6. def main():
  7. try:
  8. bar('0')
  9. except Exception as e:
  10. logging.exception(e)
  11. main()
  12. print('END')
  13. # 结果
  14. ERROR:root:division by zero
  15. Traceback (most recent call last):
  16. File "err_logging.py", line 13, in main
  17. bar('0')
  18. File "err_logging.py", line 9, in bar
  19. return foo(s) * 2
  20. File "err_logging.py", line 6, in foo
  21. return 10 / int(s)
  22. ZeroDivisionError: division by zero
  23. END # 继续执行了
  • 通过配置,logging还可以把错误记录到日志文件里,方便事后排查

(4.3).自定义抛出raise错误

  1. class FooError(ValueError):
  2. pass
  3. def foo(s):
  4. n = int(s)
  5. if n==0:
  6. raise FooError('invalid value: %s' % s)
  7. return 10 / n
  8. foo('0')
  9. # 结果
  10. Traceback (most recent call last):
  11. File "err_throw.py", line 11, in <module>
  12. foo('0')
  13. File "err_throw.py", line 8, in foo
  14. raise FooError('invalid value: %s' % s)
  15. __main__.FooError: invalid value: 0

5. 调试

方法 优点 缺点
print方法 简单使用 重复多
assert 简单使用 重复多
logging 不会抛出错误,可以输入文档 重复多
pdb python内置调试器 重复多
VS Code 强强强

(5.1).print方法(略过)

(5.2).断言assert

凡是用print()来辅助查看的地方,都可以用断言(assert)来替代:

  1. def foo(s):
  2. n = int(s)
  3. assert n != 0, 'n is zero!'
  4. return 10 / n
  5. def main():
  6. foo('0')
  7. # 结果
  8. Traceback (most recent call last):
  9. ...
  10. AssertionError: n is zero!

assert的意思是,表达式n != 0应该是True,否则,根据程序运行的逻辑,后面的代码肯定会出错。如果断言失败,assert语句本身就会抛出AssertionError

(5.3).logging

print()替换为logging是第3种方式,和assert比,logging不会抛出错误,而且可以输出到文件:

  1. import logging
  2. logging.basicConfig(level=logging.INFO)
  3. s = '0'
  4. n = int(s)
  5. logging.info('n = %d' % n)
  6. print(10 / n)
  7. # 结果
  8. INFO:root:n = 0
  9. Traceback (most recent call last):
  10. File "err.py", line 8, in <module>
  11. print(10 / n)
  12. ZeroDivisionError: division by zero

logging可以指定不同信息的级别

  • debug
  • info
  • warning
  • error

(5.4).pdb.set_trace()

我们只需要import pdb,然后,在可能出错的地方放一个pdb.set_trace(),就可以设置一个断点

  1. import pdb
  2. s = '0'
  3. n = int(s)
  4. # 暂停并进入pdb调试环境
  5. pdb.set_trace() # 运行到这里会自动暂停
  6. print(10 / n)

(5.5). IDE

直接在代码中设置断掉调试

graph TD;
A-->B;
A-->C;
B-->D;
C-->D;

Python语法基础-函数,类以及调试处理的更多相关文章

  1. python语法基础-函数-内置函数和匿名函数-长期维护

    ##################     内置函数        #######################  """ 一共是 68个内置函数: 反射相关的内置函 ...

  2. python语法基础(类)

    一.什么是类? 类是具有相同属性的一类事物 类还有功能和属性,属性就是这类事物的特征,而功能就是它能做什么,也是就是方法或者函数. 在python中类用关键词class来声明 二.类的声明 类的声明方 ...

  3. python语法基础-函数-装饰器-长期维护

    ######################################################### # 装饰器 # 装饰器非常重要,面试Python的公司必问, # 原则:开放封闭原则 ...

  4. python语法基础-函数-进阶-长期维护

    ###############    函数的命名空间和作用域    ############## """ # 函数进阶 命名空间和作用域 命名空间 全局命名空间——我们自 ...

  5. python语法基础-函数-递归函数-长期维护

    ###############    递归   ############## # 递归的定义——在一个函数里再调用这个函数本身 # 递归的最大深度——998 # 二分查找算法 # 你观察这个列表,这是 ...

  6. python语法基础-函数-迭代器和生成器-长期维护

    ###############    迭代器    ############## """ 迭代器 这是一个新的知识点 我们学习过的可以迭代的对象有哪些? list str ...

  7. python语法基础-函数-基础-长期维护

    ###############    函数的定义调用,返回值和返回值接收    ############## def mylen(): s = "myname" i = 0 for ...

  8. (数据分析)第02章 Python语法基础,IPython和Jupyter Notebooks.md

    第2章 Python语法基础,IPython和Jupyter Notebooks 当我在2011年和2012年写作本书的第一版时,可用的学习Python数据分析的资源很少.这部分上是一个鸡和蛋的问题: ...

  9. python语法基础笔记

    本篇笔记基于博主自己的的学习,理解,总结所写.很多东西可能存在误解,不能保证百分之百的正确. 1. 数据表达1.1 常量和变量1.2 数据类型1.2.1 基本数据元素1.2.1.1 数字1.2.1.2 ...

随机推荐

  1. [转] javascript 保留两位小数 (且不四舍五入)

    本文转自:https://blog.csdn.net/qq_40171039/article/details/79729503 保留两位小数且不四舍五入: 方法一: var a = 2.461; va ...

  2. 如何把ASP.NET MVC项目部署到本地IIS上

    默认情况下,在VisualStudio中开发网站,会运行在IISExpress中,如果想把网站部署到本地的IIS服务器上该怎么办呢? 一.首先,以管理员身份运行VisualStudio,否则在修改项目 ...

  3. Java设计模式 - 单例模式详解(扩展)

    单例模式引发相关整理 如何破坏单例模式 示例: /** * 如果破坏单例模式 * * @author sunyang * @date 2018/11/13 20:14 */ public class ...

  4. couldn't resolve host api.weixin.qq.com

    1.代理服务器突然出现 couldn't resolve host api.weixin.qq.com 不知原因 2.重启nginx无效-----代码肯定没有动过(之前出现过,过了一天恢复) 3.防火 ...

  5. memcache 相关

    1.查看memcache是否启动 ps -ef | grep mem

  6. Docker compose 调用外部文件及指定hosts 例子

    cat docker-compose.yml version: '3.4' services: klvchen: image: ${IMAGE_NAME} restart: always # dock ...

  7. Android为TV端助力 fragment 的用法以及与activity的交互和保存数据的方法,包括屏幕切换(转载)!

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37992017 1.管理Fragment回退栈 类似与Android系统为Acti ...

  8. Android udp json+数组 --->bytes发送数据

    Android  json支持五种数据类型 String / int(float)/bool / null  / object 今天说 object  : json = new JSONObject( ...

  9. 利用顶点位移进行VR畸变校正

    VR开发的最大挑战之一是对高帧率与高分辨率结合的要求.我们通过把顶点转化为“镜头空间”,删除了需要全屏渲染的纹理,这样就可以大规模提高手机性能. 下面的技术使用谷歌的Cardboard Unity S ...

  10. leetcode-67.二进制求和

    leetcode-67.二进制求和 Points 数组 数学 题意 给定两个二进制字符串,返回他们的和(用二进制表示). 输入为非空字符串且只包含数字 1 和 0. 示例 1: 输入: a = &qu ...