跳转至

面向对象

面向对象(OOP, Object-Oriented Programing),是一种编程方式(函数式编程和面向对象编程)中的一种,可以降低代码冗余程度。python中的类是面向对象编程的一种工具,是除函数和模块之外的另一种封装逻辑和数据的方式。

类和实例的区别:

  1. python中类和实例是两种不同的对象
  2. 类是抽象的模板,实例是根据类创建出来的一个个具体的“对象”
  3. 类和实例都有独立的命名空间

面向对象的三大思想

  1. 继承
  2. 多态 基于对象接口而不是类型
  3. 封装

1. 类的定义与实例化

python中使用class关键字来定义一个类。

1
2
class 类名:
    pass

类名的命名规则:以大写字母开头

实例化一个类:实例名 = 类名(参数)

2. 类的成员

1. 属性

属性的方法方法:实例名.属性名

1.1 实例属性

由方法内部的self属性进行赋值运算产生,保存在对象(实例)中,执行只能通过对象访问。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Person:

    def __init__(self, name):
        # 1. 在类的内部定义属性
        self.name = name

tom = Person('Tom')
print(tom.name) # Tom
# 2. 在类的外部定义属性
tom.age = '18'
print(tom.age) # 18

Jerry = Person('Jerry')
print(Jerry.name) # Jerry
print(Jerry.age) # AttributeError: 'Person' object has no attribute 'age'

tom 和 Jerry两个对象属于两个不同的命名空间,给Tom添加的age属性在Jerry对象中并不能访问。

1.2 类属性

通过class语句内的赋值语句创建,保存在类中,执行时可以通过对象访问,也可以通过类访问

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Person:
    country = 'America'

    def __init__(self, name):
        self.name = name

Tom = Person('Tom')

# 通过实例访问类属性
print(Tom.country)  # America

# 通过类访问类属性
print(Person.country) # America

1.3 私有属性

在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,确保了外部代码不能随意修改对象内部的状态,但是可以间接访问。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Person:

    def __init__(self, name):
        self.__name = name
    def get_name(self):
        return self.__name
Tom = Person('Tom')
# 直接访问报错
# print(Tom.name)  # AttributeError: 'Person' object has no attribute 'name'

print(Tom.get_name()) # Tom

2. 方法

位于类中的函数称为方法。·

类方法调用的第二种方式:class.method(instance, args,...),事实上,instance.method(args...)还是会翻译成这种方式来调用

1. 构造方法

作用:

  1. 初始化实例属性
  2. 执行其他初始化任务
  3. 创建一个实例时自动运行
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Bar:
    def __init__(self,name,age):
        self.n = name
        self.a = age
        print('创建对象时自动执行构造方法')
    def foo(self):
        print(self.n,self.a)

obj = Bar("Tom",10) # 创建对象时自动执行构造方法
obj.foo() # Tom 10

2. 普通方法

保存在类中,由对象调用

1
2
3
4
5
6
7
8
class Bar:
    def __init__(self):
        pass
    def hello(self):
        print('hello world!')

obj = Bar()
obj.hello() # hello world!

3. 私有方法

1
2
3
4
5
6
7
8
9
class Bar:
    def __init__(self):
        pass
    def __hello(self):
        print('hello world!')
    def foo(self):
        self.__hello()
obj = Bar()
obj.foo()

4. self参数

类在创建的过程中会在内存中开辟一个属于类的空间,当创建一个实例的时候也会创建属于实例的空间,当实例需要执行类中的方法,回去找类空间相应的方法,方法中的self参数,接收的就是实例本身。所以self代指调用方法的实例对象

实例时方法调用的主体,并且会自动传入给方法的self参数,把实例传入给第一个参数从而告诉方法应该处理哪一个实例。

1
2
3
4
5
6
7
8
9
class Bar:
    def foo(self):
        print("self id:",id(self))

obj = Bar()
print("obj  id:",id(obj)) # obj  id: 2018867963888
obj.foo()        # self id: 2018867963888

# self 和 obj 是同一个东西

3. 继承

一个类继承另一个类时,它将自动获得另一个类的所有属性和方法;原有的类称为父类(基类),新类称为子类(派生类)。子类还可以自定义自己的属性和方法。

3.1 继承

1
2
3
4
5
6
7
8
class Animal:
    def run(self):
        print("Animal is running...")
class Cat(Animal):
    pass

Tom = Cat()
Tom.run() # Animal is running...

3.2 扩展

在父类的方法上添加新的功能。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def pay(self, base):
        print("应发工资:%s" % money)

class Manager(Person):
    def pay(self, base, commission):
        Person.pay(self, base+commission)

3.3 重写

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Animal:
    def run(self):
        print("Animal is running...")

class Cat(Animal):
    def run(self):
        print('Cat is running...')

Tom = Cat()
Tom.run() # Cat is running...

3.4 组合

一个类A中,使用另一个类B的实例对象作为类A的属性。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Date():
    '''date'''
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

class Person:
    def __init__(self, name, date):
        self.name = name
        self.birthday = date
    def show_birthday(self):
        print("%s:%s-%s-%s" %(self.name, self.birthday.year, self.birthday.month, self.birthday.day))

Jerry = Person('Jerry',Date('1999','2','14'))
Jerry.show_birthday() # Jerry:1999-2-14

3.5 super()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class A:
    def func(self):
        print("This is A")

class B(A):
    def func(self):
        super().func()
        # A.func(self) # 另一种的写法
        print("This is B")

b = B()
b.func()  
# 输出结果
# This is A
# This is B

super安装MRO顺序来寻找当前类的下一个类

python2新式类:super(子类名,self).函数名()

3.6 抽象类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from abc import ABCMeta, abstractclassmethod

class Super(metaclass=ABCMeta):
    def __init__(self):
        pass

    @abstractclassmethod
    def action(self):
        pass

class Sub(Super):
    def action(self):
        print("hello")

x = Sub()
x.action() # hello

action函数必须在子类中定义,否则会报错 TypeError: Can't instantiate abstract class Sub with abstract methods action

3.7 多继承

  1. 从左到右
  2. 从下到上
  3. 广度优先

例1:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class F:
    def bar(self):
        print('F.bar')

class F1(F):
    def boo(self):
        print('F1.bar')

class F2:
    def bar(self):
        print('F2.bar')

class S(F1,F2):            #从下往上找
    pass

obj = S()
obj.bar()
#############
F.bar

例2:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class F1:
    def bar(self):
        print('F1.bar')

class F2:
    def bar(self):
        print('F2.bar')

class S(F2,F1):         #从左往右找
    pass

obj = S()
obj.bar()
###########
F2.bar

例3:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Base:
    def bar(self):
        print('Base.bar')

class F(Base):
    def foo(self):
        print('F.bar')

class F1(F):
    def foo(self):
        print('F1.bar')

class F2(Base):
    def bar(self):
        print('F2.bar')

class S(F1,F2):
    pass

obj = S()
obj.bar()   #有共同的父类,最后在父类中找
############
F2.bar

3.7 经典类和新式类

python2.x:默认是经典类,搜索算法为DFLR(Depth First,Left to Right),深度优先。继承object后是新式类

python3.x:没有经典类,都是新式类,隐式继承object类,搜索算法为 MRO(method resolution order),广度优点。

4. 多态和多态性

Python3.7之多态与多态性 - 彩虹然 - 博客园 (cnblogs.com)

多态:多态是指运算的意义取决于运算的对象。(也就是说,代码不应该关心它处理的对象是什么,而应该关注这个对象做什么)。多态可以用来隐藏接口差异性。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Animals:
    def talk(self):
        pass


class Cat(Animals):
    def talk(self):
        print("Meow...")


class Dog(Animals):
    def talk(self):
        print("Woof!...")


class Person(Animals):
    def talk(self):
        print("Ying...")


def func(animal):
    # 不用关心是什么动物,只要是个动物就应该叫。
    animal.talk()


xiaoming = Person()
xiaohei = Cat()
xiaohuang = Dog()

for i in [xiaoming, xiaohei, xiaohuang]:
    func(i)

6. 装饰器

6.1 @property

把方法变成属性。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Bar:
    def __init__(self):
        pass
    # 属性
    @property
    def foo(self):
        return 1

    @foo.setter
    def foo(self,val):
        print(val,"setter")

    @foo.deleter
    def foo(self):
        print("delete")

obj = Bar()
obj.foo=123  #执行赋值语句将执行@foo.setter下对应的方法
del obj.foo #执行@foo.deleter下对应的方法

############
setter
delete

例1:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Page:
    def __init__(self,page):
        try:
            p = int(page)
        except Exception as e:
            p = 1
        self.page = p
    @property
    def start(self):
        val = (self.page-1) * 10
        return val
    @property
    def end(self):
        val = self.page * 10
        return val

li = []
for i in range(1000):
    li.append(i)
while True:
    p = input("请输入页码:")
    if p == "q":
        break
    obj = Page(p)
    print(li[obj.start:obj.end])

例2:属性的另一种表示方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Bar:

    def f1(self):
        return 1
    def f2(self,val):
        print('f2:',val)
    def f3(self):
        print("f3:del")
    per = property(fget=f1,fset=f2,fdel=f3,doc="注释")

obj = Bar()
print(obj.per)
obj.per = 123
del obj.per
###########
f2: 123
f3:del

6.2 @staticmethod

静态方法,不需要创建对象,不会隐式传递self。实例对象和类对象都可以调用。当方法和实例无相关性时使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import time
class Person:
    country = 'China'

    def __init__(self,name,age):
        self.name = name
        self.age = age
    @staticmethod
    def show_time():
        print(time.strftime("%Y-%m-%d %H:%M:%S" ))

Person.show_time() # 2020-02-15 19:17:53
Tom = Person('Tom', 18)
Tom.show_time() # 2020-02-15 19:17:53

6.3 @classmethod

第一个参数必须是当前类对象,该参数名一般约定为cls,通过它来传递类的属性和方法。实例对象和类对象都可以调用。在实例化之前修改类属性的时候使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import time
class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    @classmethod
    def today(cls):
        struct_time = time.localtime()
        date = cls(struct_time.tm_year, struct_time.tm_mon, struct_time.tm_mday) # 自己类的实例化
        return date

today = Date.today()
print(today.year,today.month,today.day) # 2020 2 15

7. 反射

用字符串数据类型的名字,来操作这个名字对应的函数,示例变量,绑定方法等。

反射的使用场景:

  1. 反射对象的实例变量、绑定方法
  2. 反射类中的静态变量、绑定方法、其他方法
  3. 反射模块中的所有变量

7.1 反射对象的实例变量、绑定方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def foo(self):
        return 'hello world!'

zhangsan = Person('张三', 18)

# 1.反射对象的实例变量
print(getattr(zhangsan, 'name'))

# 2.反射对象的绑定方法
print(getattr(zhangsan, 'foo')())

# 输出结果
# 张三
# hello world!

7.2 反射类中的静态变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Person:
    country = 'China'
    def __init__(self):
        pass
    def foo(self):
        pass

print(getattr(Person, 'country'))
# 输出结果
# China

7.3 反射模块中的变量

1.反射模块中的变量

1
2
3
import module
getattr(module,'func')
getattr(module,'Bar')

2.反射脚本(本文件)中的变量

1
2
import sys
getattr(sys.modules['__main__'],func)

7.4 hasattrsetattrdelattr

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class Person:
    country = 'China'
    def __init__(self):
        pass
    def foo(self):
        pass

Bob = Person()
# hasattr 判断是否有某个成员
print(hasattr(Bob, 'name')) # False
# setattr 设置属性
setattr(Bob, 'name', 'Bob')
print(hasattr(Bob, 'name')) # True
# delattr 删除属性
delattr(Bob, 'name')
print(hasattr(Bob, 'name')) # False

7.5 反射的例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python
# -*- coding: utf-8  -*-
while True:
    inp = input("请输入要访问的URL:")
    if inp == "q":
        break
    class Bar:

        def f1(self):
            return "首页"

        def f2(self):
            return "新闻"

        def f3(self):
            return "博客"

    obj = Bar()
    if hasattr(obj,inp):
        f = getattr(obj,inp)
        print(f())
    else:
        print("404")

8. 元类

9. 重装运算符

9.1__new__

是一个构造方法,用来创建一个对象需要的空间。

使用场景:创建单例模式(一个类只会创建一次self的空间)

1
2
3
4
5
6
class Bar:
    __instance = None
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super().__new__(cls)
        return cls.__instance

9.1 __call__方法

1
2
3
4
5
6
7
8
9
class Bar:

    def __init__(self):
        print("init")

    def __call__(self):
        print("call")
obj = Bar()
obj() #对象加括号自动执行__call__方法

9.2 strint

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Bar:

    def __init__(self):
        pass
    def __int__(self):
        return 111
    def __str__(self):
        return "str"
obj = Bar()
print(obj,type(obj))
r = int(obj)   #int 加对象,自动执行对象的int方法,并将返回值赋值给int对象
print(r)
r = str(obj)
print(r)    #str 加对象,自动执行对象的str方法,并将返回值赋值给str对象
------------
str <class '__main__.Bar'>
str

9.3 例:__str_

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Bar:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __str__(self):
        return "%s:%s" %(self.name,self.age)

obj = Bar("Tom",18)
print(obj)
-----------
Tom:18

9.4 del 析构方法:对象销毁的时候执行

1
2
3
4
5
6
7
class Bar:

    def __init__(self):
        pass
    def  __del__(self):
        print("析构方法")
obj = Bar()

9.5 dict 将字段中的成员以字段的形式返回

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Bar:
    """注释"""
    def __init__(self,name,age):
        self.name = name
        self.age = age

obj = Bar('Tom',18)
#将字段中的成员以字段的形式返回
print(Bar.__dict__)
print(obj.__dict__)
---------
{'__module__': '__main__', '__init__': <function Bar.__init__ at 0x0000013DAD733AE8>, '__weakref__': <attribute '__weakref__' of 'Bar' objects>, '__doc__': '注释', '__dict__': <attribute '__dict__' of 'Bar' objects>}
{'name': 'Tom', 'age': 18}

9.6 getitemsetitemdelitem

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Bar:
    """注释"""
    def __init__(self):
        pass
    def __getitem__(self,item):
        return item + 10
    def __setitem__(self,k,v):
        print(k,v) 
    def __delitem__(self,val):
        print('delete:',val)
obj = Bar()
print(obj[2])
obj['name']='Tom'
del obj['del']
----------------
name Tom
delete: del

9.7 iter 如果类中有__iter__方法,其对象就是一个可迭代对象; 对象名.iter()的返回值是迭代器

1
2
3
4
5
6
7
8
9
class Bar:

    def __iter__(self):
        return iter([11,22,33])

obj = Bar()
for i in obj:
    print(i)
  #for循环执行obj对象的类中的__iter__方法,并获取其迭代器;循环上一步中返回的对象

10. 单例模式

应用场景:数据库连接池

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Bar:
    __v = None
    @classmethod
    def get_instance(cls):
        if cls.__v:
            return cls.__v
        else:
            cls.__v = Bar()
            return cls.__v
# 不用再使用Bar()创建实例
obj1 = Bar.get_instance()
print(obj1)
obj2 = Bar.get_instance()
print(obj2)
obj3 = Bar.get_instance()
print(obj3)
###########
<__main__.Bar object at 0x0000026D865B4A20>
<__main__.Bar object at 0x0000026D865B4A20>
<__main__.Bar object at 0x0000026D865B4A20>

线程安全单例模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Bar:
    from threading import Lock
    __instance = None
    lock = Lock()

    def __new__(cls, *args, **kwargs):
        with cls.lock:
            if not cls.__instance:
                cls.__instance = super().__new__(cls)
        return cls.__instance