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 """
View Code

当在函数或方法中定义异常处理的代码时,要特别注意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 抛出异常

对于我们自定义的异常,如果想要触发,则需要使用:raise MyException()类实现。

6.反射

反射,提供了一种更加灵活的方式让你可以实现去 对象 中操作成员(以字符串的形式去 对象 中进行成员的操作)

Python中提供了4个内置函数来支持反射:

  • getattr,去对象中获取成员
    v1 = getattr(对象,"成员名称")
    v2 = getattr(对象,"成员名称", 不存在时的默认值)
  • setattr,去对象中设置成员
    setattr(对象,"成员名称",值)
  • hasattr,对象中是否包含成员
    v1 = hasattr(对象,"成员名称") # True/False
  • delattr,删除对象中的成员
    delattr(对象,"成员名称")

     

反射除了获取对象中的成员以外,还可以导入对应的模块

# 导入模块
from importlib import import_module

m = import_module("random")

v1 = m.randint(1,100)

在很多项目的源码中都会有 import_modulegetattr 配合实现根据字符串的形式导入模块并获取成员

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'>

 

posted on 2022-11-08 16:29  阿明明  阅读(39)  评论(0编辑  收藏  举报