1.初识面向对象
想要通过面向对象去实现某个或某些功能时需要2步:
class Foo(object): name = 'kunmzhao' def myself(self, msg): print(msg) # hello foo = Foo() foo.myself('hello')
在上面案例中的myself函数有一个self参数,但是调用的时候为什么没有传参?
在定义类时候,所有的函数默认都会有一个参数self,该self代指的就是实例化对象本身,用于访问对象中的变量和方法,在实例化对象的时候,即对象=类(),类会指定默认函数__init__,将实例化的对象地址传递给self
class Foo(object): name = 'kunmzhao' def __init__(self): print(id(self)) # 1540192921392 def myself(self, msg): print(msg) foo = Foo() print(id(foo)) # 1540192921392
类和对象的关系
class Foo(object): name = 'kunmzhao' def __init__(self, name): self.name = name def send_msg(self): print(self.name) print(Foo.name) # kunmzhao foo = Foo('Victor') print(foo.name) # Victor
通过上面的案例我们可以发现:
- 类就是一个模板,用来创建对象,类中可以封装一个变量和方法供对象使用
- 对象是类的实例化,对象之间是相互独立的,对象中封装着具体的数据
- self参数代指的就是对象本身,通过对象调用属性或者方法就会将对象地址传递给self参数
2.面向对象中的成员
类中封装的所有成员如下:
- 变量
- 类变量
- 实例变量
- 方法
- 静态方法
- 类方法
- 绑定方法
- 默认方法
- 属性
2.1 变量
2.1.1 类变量
类变量是类中定义的变量,有确切的数值,被所有的对象所共有,可以通过类或者对象调用
class Foo(object): # 定义类变量 name = 'kunmzhao' def myself(self, msg): print(msg) # 通过类调用类变量 print(Foo.name) # kunmzhao # 通过类修改类变量 Foo.name = 'Victor' # 通过对象调用类变量 foo = Foo() print(foo.name) # Victor # 通过对象无法修改类变量,看似修改,其实是创建一个实例变量 foo.name = 'kunmzhao' print(Foo.name, foo.name) # Victor kunmzhao
2.1.2 实例变量
实例变量是封装在一个个的对象中的,是独立的,通过__init__函数实现
class Foo(object): # 定义类变量 name = 'kunmzhao' def __init__(self, age, gender): # 封装了3个实例变量 self.age = age self.gender = gender self.address = '上海市' foo = Foo(18, '男') # 实例变量只能通过对象调用 print(foo.age, foo.gender, foo.address)
提示:当所有的对象有相同的变量值的时候,我们就可以维护成一个类对象,减少内存的消耗
2.2 方法
2.2.1 绑定方法
默认有一个self参数,由对象调用,可在绑定方法中可以调用所有的变量和方法
class Foo(object): name = 'kunmzhao' def __init__(self, age, gender): self.age = age self.gender = gender self.address = '上海市'
# 定义绑定方法 def myfunc(self): print(self.age, self.gender, self.address) foo = Foo(18, '男') # 对象调用绑定方法 foo.myfunc()
2.2.2 类方法
默认有一个cls参数,类和对象都可以直接调用,在该方法中,无法调用绑定方法和实例变量,因为没有self参数,但可以调用类方法和类变量
类方法的定义通过@classmethod
class Foo(object): name = 'kunmzhao' def __init__(self, age, gender): self.age = age self.gender = gender self.address = '上海市' def myfunc(self): print(self.age, self.gender, self.address) # 定义类方法 @classmethod def classfunc(cls): print(cls.name) foo = Foo(18, '男') # 通过对象调用类方法 foo.classfunc() # kunmzhao # 通过类调用类方法 Foo.classfunc() # kunmzhao
2.2.3 静态方法
没有默认参数,类和对象都可以直接调用,在方法中无法调用绑定方法,类方法,类变量或者实例变量
class Foo(object): name = 'kunmzhao' def __init__(self, age, gender): self.age = age self.gender = gender self.address = '上海市' def myfunc(self): print(self.age, self.gender, self.address) @classmethod def classfunc(cls): print(cls.name) # 定义静态方法 @staticmethod def staticfunc(): print('我是一个静态方法') foo = Foo(18, '男') # 通过对象调用静态方法 foo.staticfunc() # 我是一个静态方法 # 通过类调用静态方法 Foo.staticfunc() # 我是一个静态方法
2.2.4 默认方法
- __init__:用于初始化对象的方法,将对象需要的参数传递进去
- __new__:构造方法,在__init__函数之前调用,用于开辟对象的内存空间
class Foo(object): def __new__(cls, *args, **kwargs): print('1.执行new方法') return super().__new__(cls) def __init__(self, name, *args, **kwargs): print("2.执行init方法") foo = Foo(123)
- __call__:对象()的时候调用
class Foo(object): def __call__(self, *args, **kwargs): print('hello') foo = Foo() foo() # hello
- __str__:对象在通过print输出时候调用
class Foo(object): def __str__(self): return 'hello' foo = Foo() print(foo) # hello
- __getitem__/__setitem__/__delitem__:可以通过[]获取/设置/删除数据
class Foo(object): def __init__(self): self.var_dict = {} def __getitem__(self, item): return self.var_dict[item] def __setitem__(self, key, value): self.var_dict[key] = value def __delitem__(self, item): del self.var_dict[item] foo = Foo() foo['name'] = 'kunmzhao' print(foo['name']) # kunmzhao del foo['name']
- __enter__/__exit__:同于创建上下文管理器
class Foo(object): def __enter__(self): pass def __exit__(self, exc_type, exc_val, exc_tb): pass
# 面试题(补充代码,实现如下功能)
class Context: def do_something(self): print('内部执行') with Context() as ctx: print('内部执行') ctx.do_something()
class Context: def do_something(self): print('内部执行') def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): pass with Context() as ctx: print('内部执行') ctx.do_something()
- __iter__/ __next__:前者用于返回一个可迭代对象,后者用于返回下一个数据
- 迭代器
迭代器类型的定义:
1.当类中定义了 __iter__ 和 __next__ 两个方法。
2.__iter__ 方法需要返回对象本身,即:self
3. __next__ 方法,返回下一个数据,如果没有数据了,则需要抛出一个StopIteration的异常。class IT(object): def __init__(self): self.counter = 0 def __iter__(self): return self def __next__(self): self.counter += 1 if self.counter == 3: raise StopIteration() return self.counter
- 可迭代对象
如果一个类中有__iter__方法且返回一个迭代器对象 ;则我们称以这个类创建的对象为可迭代对象
# 基于可迭代对象&迭代器实现:自定义range class IterRange(object): def __init__(self, num): self.num = num self.counter = -1 def __iter__(self): return self def __next__(self): self.counter += 1 if self.counter == self.num: raise StopIteration() return self.counter class Xrange(object): def __init__(self, max_num): self.max_num = max_num def __iter__(self): return IterRange(self.max_num) obj = Xrange(100) for item in obj: print(item)
- 迭代器
2.3 属性
属性就是绑定方法+装饰器的组合使用,可以让我们在以后调用的方法的时候不需要加括号了,也就是方法当变量使用
class Foo(object): def __init__(self, name): self.name = name def f1(self): return 123 @property def f2(self): return 123 obj = Foo("kunmzhao") v1 = obj.f1() print(v1) # 123 v2 = obj.f2 print(v2) # 123
基于属性有两种写法
方法一:基于装饰器
class Foo(object): @property def f1(self): return 123 @f1.setter def f1(self, value): print('设置f1') pass @f1.deleter def f1(self): print('删除f2') pass foo = Foo() print(foo.f1) # 123 foo.f1 = 123 # 设置f1 del foo.f1 # 删除f2
方式二: 基于方法property
class Foo(object): def f1(self): return 123 def setf1(self, value): print('设置f1') pass def delf1(self): print('删除f2') pass x = property(f1, setf1, delf1) foo = Foo() print(foo.x) # 123 foo.x = 123 # 设置f1 del foo.x # 删除f2
3.成员修饰符
python中成员修饰符就是公有和私有
- 公有:在任何任何地方都可以被调用,类中,对象中,继承类中等
- 私有:成员以双下划线开头__,只有本类内部中可以调用,子类中也无法继承
class Foo(object): __data = 'name' def show(self): print(self.__data) # 无法通过类直接调用 # Foo.__data # 无法通过对象直接调用 # Foo().__data # 可以在类中使用 Foo().show() # name
4.面向对象的三大特性
4.1 封装
封装主要体现在两个方面:
- 将很多的方法封装在一个类中
- 将数据通过__init__封装在对象中
4.2 继承
4.2.1继承的定义方法
当我们使用子类对象去调用某个方法的时候,会优先在实例化对象的类中寻找,如果没有,则根据继承关系向上寻找
class Base: def func(self): print("Base.func") class Son(Base): def show(self): print("Son.show") s1 = Son() s1.show() s1.func() # 优先在自己的类中找,自己没有才去父类。 s2 = Base() s2.func()
4.2.2 继承顺序
在python2版本中,有两种继承顺序,一种是经典类,一种是新式类
- 经典类:在定义类的时候,不指定继承类,该类的继承顺序为广度优先
class Foo(): pass
- 新式类:指定继承类,该类的继承顺序为深度优先
class Foo(object): pass
在python3中则只保留了新式类,去除了经典类
总结继承顺序口诀:从左到右,深度优先,大小钻石,留住顶端
继承顺序为:A->B->D->G->H->K->C->E->F->M->N->P->object
当然我们在实际开发中,如果想快速的了解某个类的继承顺序,则可以使用mro方法
class M: pass class N: pass class E(M): pass class G: pass class K: pass class H(K): pass class D(G, H): pass class F(M, N): pass class P: pass class C(E, F): pass class B(D, E): pass class A(B, C, P): pass print(A.mro()) # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.G'>, <class '__main__.H'>, <class '__main__.K'>, <class '__main__.C'>, <class '__main__.E'>, <class '__main__.F'>, <class '__main__.M'>, <class '__main__.N'>, <class '__main__.P'>, <class 'object'>]
4.2.3 super()关键字作用:
会按照继承顺序,向上寻找方法执行
class Foo(object): def __new__(cls, *args, **kwargs): print('执行object类的new方法') return super().__new__(cls, *args, **kwargs) Foo()
4.3 多态
在python中的,对局数类型不需要声明,是天生支持多态的,我们关注的是对象本身能够做什么,而不是在意其本身的数据类型,也被称之为鸭子类型
5.异常处理
在实际开发中,我们希望在某些报错的地方,将错误处理一下,保证整体程序可以继续运行下去,我们就需要捕获异常,当然我们也可以自定义异常或者抛出异常
5.1 捕获异常
常用格式一:捕获所有异常
try: # 逻辑代码 except Exception as e: # try中的代码如果有异常,则此代码块中的代码会执行。
常用格式二:捕获异常并做处理
try: # 逻辑代码 except Exception as e: # try中的代码如果有异常,则此代码块中的代码会执行。 finally: # try中的代码无论是否报错,finally中的代码都会执行,一般用于释放资源。
常用格式三:分类捕获异常
import requests from requests import exceptions while True: url = input("请输入要下载网页地址:") try: res = requests.get(url=url) print(res) except exceptions.MissingSchema as e: print("URL架构不存在") except exceptions.InvalidSchema as e: print("URL架构错误") except exceptions.InvalidURL as e: print("URL地址格式错误") except exceptions.ConnectionError as e: print("网络连接错误") except Exception as e: print("代码出现错误", e)
Python中内置了很多细分的错误,供你选择。
1 常见异常: 2 """ 3 AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x 4 IOError 输入/输出异常;基本上是无法打开文件 5 ImportError 无法引入模块或包;基本上是路径问题或名称错误 6 IndentationError 语法错误(的子类) ;代码没有正确对齐 7 IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问n x[5] 8 KeyError 试图访问字典里不存在的键 inf['xx'] 9 KeyboardInterrupt Ctrl+C被按下 10 NameError 使用一个还未被赋予对象的变量 11 SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了) 12 TypeError 传入对象类型与要求的不符合 13 UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量, 14 导致你以为正在访问它 15 ValueError 传入一个调用者不期望的值,即使值的类型是正确的 16 """ 17 更多异常: 18 """ 19 ArithmeticError 20 AssertionError 21 AttributeError 22 BaseException 23 BufferError 24 BytesWarning 25 DeprecationWarning 26 EnvironmentError 27 EOFError 28 Exception 29 FloatingPointError 30 FutureWarning 31 GeneratorExit 32 ImportError 33 ImportWarning 34 IndentationError 35 IndexError 36 IOError 37 KeyboardInterrupt 38 KeyError 39 LookupError 40 MemoryError 41 NameError 42 NotImplementedError 43 OSError 44 OverflowError 45 PendingDeprecationWarning 46 ReferenceError 47 RuntimeError 48 RuntimeWarning 49 StandardError 50 StopIteration 51 SyntaxError 52 SyntaxWarning 53 SystemError 54 SystemExit 55 TabError 56 TypeError 57 UnboundLocalError 58 UnicodeDecodeError 59 UnicodeEncodeError 60 UnicodeError 61 UnicodeTranslateError 62 UnicodeWarning 63 UserWarning 64 ValueError 65 Warning 66 ZeroDivisionError 67 """
当在函数或方法中定义异常处理的代码时,要特别注意finally和return。
def func(): try: return 123 except Exception as e: pass finally: print(666) func()
在try或except中即使定义了return,也会执行最后的finally块中的代码。
5.2 自定义异常
在开发中也可以自定义异常,方法如下:
class MyException(Exception): pass
自定义异常的抛出捕获
class MyException(Exception): title = "请求错误" try: raise MyException() except MyException as e: print("MyException异常被触发了", e.title) except Exception as e: print("Exception", e)
5.3 抛出异常
6.反射
v1 = getattr(对象,"成员名称") v2 = getattr(对象,"成员名称", 不存在时的默认值)
setattr(对象,"成员名称",值)
v1 = hasattr(对象,"成员名称") # True/False
delattr(对象,"成员名称")
# 导入模块 from importlib import import_module m = import_module("random") v1 = m.randint(1,100)
from importlib import import_module path = "openpyxl.utils.exceptions.InvalidFileException" module_path, class_name = path.rsplit(".", maxsplit=1) # "openpyxl.utils.exceptions" "InvalidFileException" module_object = import_module(module_path) cls = getattr(module_object, class_name) print(cls) # <class 'openpyxl.utils.exceptions.InvalidFileException'>