什么时候用面向对象: 当要使用一堆相同的值且去定义不同的函数的时候(即: 多个函数公用相同的参数的时候),就用面向对象去实现(如果还是不懂可以看回 老男孩Python全栈完整视频第9期 -> 第二部分 -> 数据库day63中的面向对象的回顾

面向对象的三大特性: 继承 多态 封装

1.

  • 什么是类: 知道里面有什么属性和方法,但又不知道里面有什么具体的值 -> list dict tuple set() …… 这些都可以称之为类

  • 类是由type创建的,而对象是由类创建的

  • 通过 class 关键字初始化一个类

    • 写法一

class Person:
    pass

p = Person()
print(p) # <__main__.Initial object at 0x000001DEDE653518>

    • 写法二 -> 当我们初始化一个类的时候默认继承了 object 类

class Person(object):
    pass

p = Person()
print(p)  # <__main__.Initial object at 0x000001DEDE653518>

2. 类的方式

  • 方式一

    • 通过class创建类

    • 通过class创建的类实际上内部也是调用了type帮助我们创建了类(即:方式二)

class Foo:
    def __init__(self):
        self.name = 'Kevin'

    def fun(self):
        return 123


obj = Foo()

print(obj.name)  # Kevin
print(obj.fun())  # 123

  • 方式二

    • 通过type创建类

    • 格式: type('类名', ('所要继承的类',), {'属性名': '属性值', ……})

Foo = type('Foo', (object,), {'name': 'Kevin', 'fun': lambda self: 123})

obj = Foo()

print(obj.name)  # Kevin
print(obj.fun())  # 123

3. __init__ 函数 -> 初始化方法 -> 接收外面所传递进来的参数

class Person:
    def __init__(self, name, age):
        print(name, age)

p = Person('Kevin', 22)

4. 类的属性

  • self -> 定义类的属性

    • self是__init__函数的第一个参数,self是可以起别的名字,但最好不要这么做因为 self 这个名字是约定俗成

    • self 的作用就是定义类的属性,且 self 就相当于一个字典

    • self表示一个具体的实例本身(即: 实例化出来的对象 等同于 self),且self和js里面的this很类似,谁调用self那么self就指向那个实例对象

    • self  cls 的区别: self表示一个具体的实例本身(即: 实例化出来的对象 等同于 self)cls表示当前这个类的本身

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person('Kevin', 22)

  • 获取类的属性值

    • 通过 对象.属性名 获取类的属性

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person('Kevin', 22)
print(p.name)
print(p.age)

  • 修改类的属性值

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person('Kevin', 22)

p.name = 'Yeung'

print(p.__dict__)  # {'name': 'Yeung', 'age': 22}

  • 添加类的属性值

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person('Kevin', 22)

print(p.__dict__)  # {'name': 'Kevin', 'age': 22}

p.height = 175

print(p.__dict__)  # {'name': 'Kevin', 'age': 22, 'height': 175}

  • 删除类的属性值

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person('Kevin', 22)

del p.age

print(p.__dict__)  # {'name': 'Kevin'}

5. 对象

  • 什么是对象:知道里面有具体的值 -> 例如:{'key': 123} 这就是一个对象

  • 对象是由类创建的,而类是由type创建的

  • 对象就是实例,实例就是对象
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person('Kevin', 22)  # 而 p 接收到的就是一个对象

6. 实例化

  • 将类的返回值进行赋值的这一过程就是实例化 -> 通常我们所说的实例化一个对象是因为类返回的就是一个对象 -> 实例化的时候一定会调用 __init__ 方法

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person('Kevin', 22)  # 进行实例化

  • 在模块和包中的 import xxx 这一过程也可以理解为实例化

7. 类的静态属性(公共属性)

  • 所有对象都共用一个属性的时候就将该属性设置为静态属性(例如:创建了一个人的类,然后实例化了很多人的对象,而每个人的对象都拥有中国这个属性,那么就可以将中国设置为静态属性)

  • 类的静态属性,只有在创建类的时候进行第一次赋值,后面的序列化对象的时候,静态属性都不会再进行赋值了

  • 类的静态属性可以在不进行实例化的时候获取到类的属性

  • 类的静态属性不能同通过 .__dict__ 进行修改

  • 获取静态属性的方法一: 类名.静态属性名

class Person:
    static_attr = '类的静态属性'

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

print(Person.static_attr)  # '类的静态属性'

  • 获取静态属性的方法二: 对象.静态属性名 -> 不推荐使用,不合理

class Person:
    static_attr = '类的静态属性'

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

p = Person('Kevin', 18)
print(p.static_attr)  # '类的静态属性'

  • 在类中使用静态属性的方法一: 类名.静态属性名 -> 推荐使用

class Person:
    static_attr = '类的静态属性'

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

    def fn(self):
        print(Person.static_attr)  # 在类中使用静态属性

p = Person('Kevin', 18)
p.fn()

  • 在类中使用静态属性的方法二: self.静态属性名

class Person:
    static_attr = '类的静态属性'

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

    def fn(self):
        print(self.static_attr)  # 在类中使用静态属性

p = Person('Kevin', 18)
p.fn()

  • 如果静态变量是不可变的数据类型,且要对静态变量进行修改最好使用 类名.静态变量名 = xxx ,因为如果使用了 对象.静态变量名 = xxx 它修改的只是对象的命名空间里面和静态变量名重名的值或者在对象命名空间里面创建一个和静态变量名一样名字再将值赋给它

class Person:
    static_attr = '类的静态属性'

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

Person.static_attr = '修改了类的静态属性'

print(Person.static_attr)  # '修改了类的静态属性'

p = Person('Kevin', 18)

p.static_attr = '改回了类的静态属性'

print(Person.static_attr)  # 修改了类的静态属性

print(p.__dict__)  # {'name': 'Kevin', 'age': 18, 'static_attr': '改回了类的静态属性'}

  • 如果静态变量是可变的数据类型,且要对静态变量进行修改 类名.静态属性名 = xxx 或者 对象.静态属性名 = xxx 都可以使用

class Person:
    static_attr = ['类的静态属性']

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

Person.static_attr[0] = '修改了类的静态属性'

print(Person.static_attr[0])  # '修改了类的静态属性'

p = Person('Kevin', 18)

p.static_attr[0] = '改回了类的静态属性'

print(Person.static_attr[0])  # '改回了类的静态属性'

  • 例子: 统计实例化次数

class Person:
    count = 0

    def __init__(self):
        Person.count += 1

p1 = Person()
p2 = Person()
p3 = Person()
p4 = Person()

print(p1.count, p2.count, p3.count, p4.count, Person.count)  # 4 4 4 4 4

8. 方法

  • 定义类的方法

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def class_fn(self):
        print(self)
        print('类的方法')

p = Person('Kevin', 22)

  • 方法中 self 参数的作用就是接受外部所传入的类所返回的对象,从而可以在方法中使用该对象里面的值或方法(另外一种意思: 在方法中使用类的属性和类的其他方法)

  • 调用类的方法一 -> 类名.方法名(类所返回的对象, 其他参数一, 其他参数二)

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def other_fn(self):
        print('其他方法')

    def class_fn(self, other_data):
        print(self.name)  # 调用类中的属性
        print(self.age)  # 调用类中的属性
        print(other_data)  # 获取到的参数
        print('类的方法')
        self.other_fn()  # 调用类中的其他方法

p = Person('Kevin', 22)

Person.class_fn(p, '其他参数')  # 使用类中的方法一

  • 调用类的方法二 -> 对象.类的方法名(其他参数一, 其他参数二) -> 不用将类所返回的对象传入,使用该方式默认将类所返回的对象传入到方法中的 self 中 -> 推荐使用

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def other_fn(self):
        print('其他方法')

    def class_fn(self, other_data):
        print(self.name)  # 调用类中的属性
        print(self.age)  # 调用类中的属性
        print(other_data)  # 获取到的参数
        print('类的方法')
        self.other_fn()  # 调用类中的其他方法

p = Person('Kevin', 22)

p.class_fn('其他参数')  # 使用类中的方法二

9. 什么是函数?什么是方法?

  • 一般情况

    • 写在模块中的叫函数,写在类中的叫方法

  • 特殊情况

    • 写法: 对象.fn() -> 叫方法

from types import FunctionType, MethodType


class Foo(object):
    def fn(self):
        return '值'


obj = Foo()

# 验证是否是函数
print(isinstance(obj.fn, FunctionType))  # False
# 验证是否是方法
print(isinstance(obj.fn, MethodType))  # True

    • 写法: 类.fn() -> 叫函数

from types import FunctionType, MethodType


class Foo(object):
    def fn(self):
        return '值'


# 验证是否是函数
print(isinstance(Foo.fn, FunctionType))  # True
# 验证是否是方法
print(isinstance(Foo.fn, MethodType))  # False

10. __dict__

  • 查看类中的所有属性和方法

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def class_fn(self, other_data):
        print(self.name)
        print(self.age)
        print(other_data)
        print('类的方法')

p = Person('Kevin', 22)

print(Person.__dict__) # {'__module__': '__main__', '__init__': <function Person.__init__ at 0x000002439FA29D08>, 'class_fn': <function Person.class_fn at 0x000002439FA29C80>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

  • 查看类中的所有属性

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def class_fn(self, other_data):
        print(self.name)
        print(self.age)
        print(other_data)
        print('类的方法')

p = Person('Kevin', 22)

print(p.__dict__) # {'name': 'Kevin', 'age': 22}

  • 通过 __dict__ 对类的属性进行增删改查


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person('Kevin', 22)

print(p.__dict__)  # {'name': 'Kevin', 'age': 22}

p.__dict__['height'] = 175

print(p.__dict__)  # {'name': 'Kevin', 'age': 22, 'height': 175}


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person('Kevin', 22)

print(p.__dict__)  # {'name': 'Kevin', 'age': 22}

p.__dict__.pop('age')

print(p.__dict__)  # {'name': 'Kevin'}


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person('Kevin', 22)

print(p.__dict__)  # {'name': 'Kevin', 'age': 22}

p.__dict__['age'] = 18

print(p.__dict__)  # {'name': 'Kevin', 'age': 18}


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person('Kevin', 22)

print(p.__dict__['name'])  # Kevin

11. 类的命名空间和对象的命名空间

  • 单个类的类命名空间和对象命名空间

class Person:
    static_attr = '类的静态属性'

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

    def func(self):
        pass

p = Person('Kevin', 18)

print(p.static_attr)


    • p.static_attr 首先会在 p 对象的命名空间 里面找有没有static_attr,如果没有找到再到 Person类的命名空间 里面找,如果还没有就会报错

  • 关于继承的类名空间和对象命名空间

class A(object):
    def __init__(self):
        a = 1
        pass

    def fn(self):
        print(self.a)


class B(A):
    a = 2
    pass


b = B()

b.fn()  # 2 ,因为B类继承于A类,当 B 类所实例化出来的对象执行fn方法的时候,就会在 B 类中查找是否有 a 属性,如果没有就会在父类中查找有没有 a 属性,如果还没有就会报错

class A(object):

    def fn(self):
        print(self.a)


class B(A):
    a = 2
    pass


b = B()

b.fn()  # 2 , 因为B类继承于A类,当 B 类所实例化出来的对象执行fn方法的时候,就会在 B 类中查找是否有 a 属性,如果没有就会在父类中查找有没有 a 属性,如果还没有就会报错

12. 组合

  • 一个对象的属性值是另外一个类的对象

class Birthday:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

class Teacher:
    def __init__(self, name, age, sex, birthday):
        self.name = name
        self.age = age
        self.sex = sex
        self.birthday = birthday  # 一个对象的属性值是另外一个类的对象

b = Birthday(1997, 3, 20)

t = Teacher('Kevin', 18, '男', b)

print(t.birthday.year)  # 1997
print(t.birthday.month)  # 3
print(t.birthday.day)  # 20

13. 绑定方法

  • 对象.方法名 -> 这一过程就是绑定方法,就是一个概念了解就可以了

14. 练习

  • 人狗大战

class Dog:
    def __init__(self, name, blood, aggr, kind):
'''
        :param name: 狗的姓名
        :param blood: 血量
        :param aggr: 攻击力
        :param kind: 品种
        '''
        self.name = name
        self.blood = blood
        self.aggr = aggr
        self.kind = kind

    def bite(self, person):
# 狗咬人,人掉血
        person.blood -= self.aggr
        if person.blood <= 0:
            person.blood = 0
            print('%s咬了%s,咬死了' % (self.name, person.name))
        else:
            print('%s咬了%s,掉了%s血,还剩%s血' % (self.name, person.name, self.aggr, person.blood))

class Person:
    def __init__(self, name, blood, aggr, sex):
'''
        :param name: 姓名
        :param blood: 血量
        :param aggr: 攻击力
        :param sex: 性别
        '''
        self.name = name
        self.blood = blood
        self.aggr = aggr
        self.sex = sex

    def attack(self, dog):
# 人打狗,狗掉血
        dog.blood -= self.aggr
        if dog.blood <= 0:
            dog.blood = 0
            print('%s打了%s,打死了' % (self.name, dog.name))
        else:
            print('%s打了%s,掉了%s血,还剩%s血' % (self.name, dog.name, self.aggr, dog.blood))

kevin = Person('Kevin', 999, 998, '男')
leopard_brother = Dog('leopard_brother', 100, 20, 'teddy')

leopard_brother.bite(kevin)  # 狗咬人
kevin.attack(leopard_brother)  # 人打狗

  • 在终端输出如下信息

# 小明,10岁,男,上山去砍柴
# 小明,10岁,男,开车去东北
# 小明,10岁,男,最爱大保健
# 老李,90岁,男,上山去砍柴
# 老李,90岁,男,开车去东北
# 老李,90岁,男,最爱大保健
# 老张…


class Person:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    def shangshan(self):
        print('%s,%s岁,%s,上山去砍柴' % (self.name, self.age, self.sex))

    def drive(self):
        print('%s,%s岁,%s,开车去东北' % (self.name, self.age, self.sex))

    def favor(self):
        print('%s,%s岁,%s,最爱大保健' % (self.name, self.age, self.sex))

ming = Person('小明', '10', '男')
ming.shangshan()
ming.drive()
ming.favor()
zhang = Person('老张', '90', '男')
zhang.shangshan()
zhang.drive()
zhang.favor()

  • 已知 半径 求 圆的周长 2*pi*r 和 圆的面积 pi*(r**2)

from math import pi

class Circle:
    def __init__(self, r):
        self.r = r

    def perimeter(self):  # 周长
        return 2 * pi * self.r

    def area(self):  # 面积
        return pi * (self.r ** 2)

c = Circle(5)  # 半径为5

print(c.perimeter())  # 31.41592653589793
print(c.area())  # 78.53981633974483