目录

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) 限制关键字参数的名字(必须传入参数名)
  • 顺序: 必选参数<---默认参数<---可变参数<---命名关键字参数<---关键字参数
# 关键字参数
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw) person('hao', 20) # name: Michael age: 30 other: {}
person('hao', 20, gener = 'M', job = 'Engineer') # name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
extra = {'city': 'Beijing', 'job': 'Engineer'}
person('Jack', 24, **extra) # 命名关键字参数
def person(name, age, *, city='Beijing', job):
print(name, age, city, job) person('Jack', 24, job = '123')
person('Jack', 24, city = 'Beijing', job = 'Engineer') # Combination
# 可变 + 关键字参数
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw) f1(1, 2, 3, 'a', 'b') # a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
f1(1, 2, 3, 'a', 'b', x=99) # a = 1 b = 2 c = 0 d = 99 kw = {'ext': None} # 默认参数 + 命名关键字参数 + 关键字参数
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw) f2(1, 2, d=99, ext=None) # a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}

2. 面向对象编程

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

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

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

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

类的组成结构

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

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

(1). 定义类

# 定义类
class Dog(object): # 定义初始化方法
def __init__(self,weight,color):
"""
self: python会把对象的地址自动传给self,不要自己传
weight, color: 接收外部属性
"""
# 定义属性
self.weight = weight
self.color = color # 魔法方法: 当只打印Dog对象的时候,就可以打印这里的东西
def __str__(self):
msg = "dog weight" + self.weight + "color" + self.color
return "哈哈哈" def getweight(self):
return self.weight
def getcolor(self):
return self.color
def setweight(self):
self.weight = 100
def setcolor(self):
self.color = "green" # 定义方法
def bark(self):
"""
self: python解释器默认把调用该class的对象地址给它
"""
print("666") def run(self):
print("777") # 创建对象
huskey = Dog(5, 'Black') # 创建一个哈士奇
keji = Dog(10, 'Green') huskey.bark() # 哈士奇叫
huskey.run() # 哈士奇跑
huskey.weight = 100 # 哈士奇属性
huskey.color = 'green'
  • self表示自己,表示调用类的对象本身
  • python中类似 __***__的方法,是魔法方法,有特殊用途

(2). 类的数据封装

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

class Dog(object):

    def __init__(self, weight, color):
self.weight = weight
self.color = color def print_dog(self):
print('%s: %s' % (self.weight, self.color))

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

(3). 访问限制

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

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

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

(4). 获取对象信息

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

2.2. 继承和多态

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

(1)为什么要用继承?

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

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

(2)什么是多态?

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

C是dog,C也是animal

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

# 首先创建对象并查看对象类型
a = list() # a是list类型
isinstance(a, list) # true
b = Animal() # b是Animal类型
isinstance(b, Animal) # true
c = Dog() # c是Dog类型
isinstance(c, Dog) # true # C既是Dog也是Animal
isinstance(c, Animal) # true # 反过来就错了
isinstance(b, Dog) # false

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

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

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

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

def run_twice(animal):
animal.run() run_twice(Animal()) # 调用animal的run
run_twice(Dog()) # 调用dog的run
run_twice(Cat()) # 调用cat的run

3. 面向对象高级编程


(1). __slots__的使用

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

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

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

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

(2). @property的使用

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

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

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

class Student(object):

    # @property将get_Score方法变成属性
@property
def score(self):
return self._score # @score.setter将set_score方法变成属性赋值
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value s = Student()
s.score = 60 # OK,实际转化为s.set_score(60)
s.score # OK,实际转化为s.get_score()
s.score = 9999 # error

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

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

class Animal(object):
pass class Bird(Animal):
pass class Parrot(Bird):
pass # 此时我们需要加入额外的功能fly
# 先定义好fly的类
class Flyable(object):
def fly(self):
print('Flying...') # 同时继承两个类
class Parrot(Bird, Flyable):
pass

(4). 定制个性化类

1): __str____repr__

让打印的object更漂亮:

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

2): __iter__

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

class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器a,b def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己 def __getitem__(self, n):
if isinstance(n, int): # n是索引
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L def __next__(self):
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > 100000: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值 for i in Fib():
print(n) # 1,1,2,3,5,.......,75025 # 因为__getitem__的作用,可以index某个值
f = Fib()
f[0] # 1 # 也可以切片
print(f[0:5]) # 1,1,2,3,5

3): __call__

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

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

4) 枚举类Enum

定义常量

from enum import Enum, unique

# unique保证没有重复值
@unique
class Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6 # 访问枚举类
print(Weekday.Mon) # Weekday.Mon
print(Weekday.Mon.value) # 1
print(Weekday(1))) # # Weekday.Mon

5) type()动态创建类Class

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

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

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

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

4. 错误处理

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

try:
print('try...')
r = 10 / int('a')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
finally:
print('finally...')
print('END') try...
except: division by string
finally...
END

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

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

import logging

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

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

class FooError(ValueError):
pass def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return 10 / n
foo('0') # 结果
Traceback (most recent call last):
File "err_throw.py", line 11, in <module>
foo('0')
File "err_throw.py", line 8, in foo
raise FooError('invalid value: %s' % s)
__main__.FooError: invalid value: 0

5. 调试

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

(5.1).print方法(略过)

(5.2).断言assert

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

def foo(s):
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n def main():
foo('0') # 结果
Traceback (most recent call last):
...
AssertionError: n is zero!

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

(5.3).logging

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

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

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

  • debug
  • info
  • warning
  • error

(5.4).pdb.set_trace()

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

import pdb

s = '0'
n = int(s)
# 暂停并进入pdb调试环境
pdb.set_trace() # 运行到这里会自动暂停
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. 用python实现红包机制

    方法一,逻辑是后一个红包的范围是[0.01,剩下的钱*2/剩下的红包数,如果最后钱不足分配给每个人,就把后几个每人分配0.01元. 主要思想就是,每个人至少能领取到0.01元. import rand ...

  2. 金三银四招聘季,这些BAT以及独角兽互联网公司官方招聘网站值得关注。(个人梳理备用:附BAT以及独角兽公司官方招聘网址)

    金三银四是一年当中的招聘最旺盛的时期,即招聘高峰期,在这个期间内有非常多名企巨头公司的放出大量的岗位信息.以博主几年的工作经验来看,在这期间找到称心如意的工作的几率大大提升,对于很多程序员来说,薪水高 ...

  3. DirBuste 使用

    https://sourceforge.net/projects/dirbuster/ 官网下载 记得安装java 运行环境 这是扫描 443 端口的数据 也可以自己写字典规则 在选择模糊查询时 下面 ...

  4. [android] 内容观察者

    拦截短信,比如当发短信的时候,就把短信读取出来,当系统的短信发生变化的时候,大叫一声,把数据发送到公共的消息邮箱里面,我们的应用通过内容观察者观察公共的消息邮箱 获取ContentResolver对象 ...

  5. 33.QT-UTF8,GBK互转

    首先需要用到QString的静态成员函数来获取字符数组: QByteArray QString::toLocal8Bit () ; //获取字节数组对象 char * QByteArray::data ...

  6. 《JavaScript高级程序设计》笔记:变量、作用域和内存问题(四)

    基本类型和引用类型的值 ECMAScript变量可能包含两种不同数据类型的值:基本类型值和引用类型值.基本类型值指的是简单的数据段,而引用类型的值指那些可能有多个值构成的对象. 动态的属性 var p ...

  7. 2018-11-21 手工翻译Vue.js源码第一步:14个文件重命名

    背景 对现有开源项目的代码进行翻译(文件名/命名/注释) · Issue #107 · program-in-chinese/overview 简单地说, 通过翻译源码, 提高项目代码可读性(对于母语 ...

  8. linux连接数过多,导致ping包丢包的问题解析

    1.首先要明确,无论是tcp, udp, raw等这些都要占用socket, 那么就涉及到连接数的问题. 所以,linux连接数的问题,不仅仅是tcp连接数. 2.查看当前系统中所有的socket 连 ...

  9. C# 实现连连看功能

    本文是利用C#实现连连看的小例子,以供学习分享使用.如有不足之处,还望指正. 思路: 初始化布局(横竖十行十列,共100个单元格,每一个格一个按钮,背景图为水果图片,随机生成) . 初始化对应棋盘(用 ...

  10. 微信小程序 text属性设置 WXSS样式

    >微信小程序的布局css样式 参考自  珺L 文字 width: fit-content;font-size:20px;      /*设置文字字号*/color:red;           ...