python面向对象

1.类

1.1引入类

面向对象:

所谓面向对象,就是在编程的时候尽可能的去模拟真实的现实世界,按照现实世界中的逻辑去处理问题,分析问题中参与其中的有哪些实体,这些实体应该有什么属性和方法。我们如何通过调用这些实体中的属性和方法去解决问题

类:用来描述具有相同的属性和方法的对象的集合。它定义了集合中每一个对象所共有的熟悉和方法,对象是类的实例

使用class创建类

类没有创建作用域(所以不需要申明作用域)

类创建命名空间(将同名的变量隔离了)

命名:

变量名的风格:没有大写字母,单词之间,使用下划线分隔

类名风格:单词首字母大写,不使用下划线

优势:

  • 不使用闭包,减少内存泄漏风险
  • 不使用闭包,实现业务直接变量隔离
  • 使用类,可以从外部访问内部变量
  • 使用类,不会因为重复执行代码,导致数据被销毁
class a: #创建类,没有创建作用域
    n=0 #统计次数
    def f():
        print(f"{a.n=}")
        a.n+=1

class b:
    n=""
    def g():
        print(f"{b.n=}")
        b.n+="s"

n="zhangsan"

a.f()
b.g()
a.f()
b.g()
a.f()
b.g()

1.2 创建类

要素:
名字
属性(变量)
方法(函数

class hourse:
    name=""
    area=0 #面积
    room_count=0 #房子数量
    price=0 #单价

    def amount(self):
        return self.m2*self.prices

1.3 使用类

类是为了解决实际问题

面向对象是为了解决设计问题

1.4.对象

对象是按照类,生成的多个相互隔离的空间

__init__:魔法方法,双下线特殊函数,在特殊的时机,由python自动调用

  • 在类实例化时,自动调用
  • 接收实例化参数
  • 第一个位置参数,是实例对象本身(self)
  • 不应该有返回值
class hourse:
    name=""
    area=0 #面积
    room_count=0 #房子数量
    price=0 #单价

    def __init__(self,a,b,c,d): #双下线特色函数:在特殊的时机,由python自动调用
        print(self,a,b,c,d)

c1=hourse(3,4,5,6) #创建对象
c2=hourse(7,8,9,0)
c3=hourse(1,2,3,4)

如何创建一个对象:

  • 通过类名()可以创建一个对象
  • c1=hourse()
  • c1就是通过hourse创建的实例化对象
  • 在创建对象的时候会自动调用__init__方法并且把对象c1传递赋值给self
  • 那么该对象c1就能获取魔法方法里面的所有实例属性

1.5.属性

概念:和变量类似,用于存储数据,可以访问

类属性:类属性在整个实例化的对象中是公用的。类属性定义在类中且在函数体之外。类属性通常不作为实例属性使用

如何修改类属性:

  • 类.属性=xxx
  • self.__class__.属性=xxx
  • cls.属性=XXX

实例属性:实例属性,在任意方法内部,以self.变量名的的方式定义的变量。实例变量只能通过对象名访问,无法通过类名访问

 1 #定义类:使用class关键字
 2 #类名:使用大驼峰命名规则
 3 class Hero:
 4     #在类中可以封装实例属性和实例方法
 5     #实例属性:需要在魔法方法(构造方法)__init__封装实例属性
 6     #该魔法方法会在对象被创建的时候自动执行,不需要手动调用
 7     #魔法方法:一般不需要手动调用,已经定义的功能方法,在某种条件下自动触发
 8     def __init__(self):
 9         #self代表对象本身,谁是对象self就是谁
10         self.name="厄斐琉斯"
11         self.hp=800
12         self.mp=300
13 
14 
15 #通过类可以创建对象
16 #对象名():实例化一个对象赋值给h1
17 #h1就是通过Hero类创建的对象
18 #对象一旦被创建,那么自动调用魔法方法__init__
19 #那么对象就会拥有3个实例属性:name,hp,mp
20 #通过对象调用实例属性 对象名.属性名
21 h1=Hero()
22 print(h1.name)
23 print(h1.hp)
24 print(h1.mp)
25 #一个类可以创建无数个对象
26 h2=Hero()
27 #那么对象就会拥有3个实例属性:名字,生命值,魔法值
28 print(h2.name)
29 print(h2.hp)
30 print(h2.mp)

如何定义实例属性:

  • 在init魔法方法中进行定义实例属性
  • 并且每一个属性前面都有self代表对象本身
  • self代表被创建的对象本身,谁是对象谁就是self
  • self.实例属性=“具体的属性值”

如何修改实例属性

  • 实例.属性=xxx
  • self.属性=XXX
class a: #创建类,没有创建作用域
    n=0 #类属性:统计次数
    def f(self):
        self.b="a" #实例属性
        print(f"{a.n=}")
        a.n+=1

说明:如果实例有属性,则使用自己的属性,没有属性则使用类属性

实例属性动态赋值

 1 class Hero:
 2     #魔法方法,初始化实例属性,加载类时自动调用
 3     def __init__(self,name,hp,mp):
 4         self.name=name
 5         self.hp=hp
 6         self.mp=mp
 7 
 8     #行为封装成实例方法
 9     def attack(self):
10         print(f"{self.name}发动一次普通攻击")
11 
12     def move(self,x,y):
13         print(f"开始移动,x的坐标为{x},y的坐标为{y}")
14 
15     #方法中也可以有返回值
16     def back_city(self,b="b"):
17         print("执行回城操作")
18         print(f"使用快捷键回城{b}")
19         return 2 #代表回城的时间2秒
20 
21     #实例方法之间可以相互调用
22     def a(self):
23         print("我是a方法")
24         #通过self.方法名()进行调用
25         self.b()
26 
27     def b(self):
28         print("我是b方法")
29 
30 h1=Hero("武器大师贾克斯",888,666)
31 h1.a()
32 print(h1.name)
33 print(h1.hp)
34 print(h1.mp)
35 h1.attack()

1.6 方法

概念:类中定义的函数。类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称,按照惯例她的名称是self(其实取什么名字不重要,只是有这个参数就行,默认系统叫做self)

定义在类中的函数,称之为方法,因为会自动传递参数

如果某方法,不接受参数,直接访问结果(以固定的方式返回数据)
可以通过装饰器,伪装成属性,可以不加括号直接读取

class hourse:
    name=""
    area=0 #面积
    room_count=0 #房子数量
    price=0 #单价

    def __init__(self,a,b,c,d): #双下线特色函数:在特色的时机,由python自动调用
        print(self,a,b,c,d)

    @property #可以通过装饰器,伪装成属性
    def amount(self):
        return self.area*self.price


print(c2.amount) #可以不加括号直接读取

1.6.1 类方法

传递类本身

class a:
    @classmethod #此装饰器修饰的方法称为类方法
    def b(cls):
        pass

1.6.2 实例方法

传递实例对象

 1 class Hero:
 2     #魔法方法,初始化实例属性,加载类时自动调用
 3     def __init__(self):
 4         self.name="岩雀"
 5         self.hp=900
 6         self.mp=300
 7     #行为封装成实例方法
 8     #类中函数就是实例方法
 9     #方法和函数的区别:方法定义在类中,而且第一个参数默认一定是self代表对象本身
10     def attack(self):
11         #在实例方法中可以使用实例属性
12         #通过self.属性名进行调用
13         print(f"{self.name}发动一次普通攻击")
14     #方法中的参数可以使用任何类型
15     #位置参数,关键字参数,默认参数,不定长参数...
16     #前提是所有参数必须在self后面进行添加
17     def move(self,x,y):
18         print(f"开始移动,x的坐标为{x},y的坐标为{y}")
19     #方法中也可以有返回值
20     def back_city(self,b="b"):
21         print("执行回城操作")
22         print(f"使用快捷键回城{b}")
23         return 2 #代表回城的时间2秒
24 
25 
26 h1=Hero()
27 print(h1.name)
28 print(h1.hp)
29 print(h1.mp)
30 #实例方法的使用:对象.方法名()
31 h1.move(280,390)
32 h1.attack()
33 print(h1.back_city("c"))
34 time=h1.back_city()
35 print(time)

类方法和类属性

 1 class Hero:
 2     #封装类属性:类中定义的变量就是类属性
 3     num=777 #num就是类属性
 4 
 5     #魔法方法,初始化实例属性,加载类时自动调用
 6     #封装的实例属性
 7     def __init__(self,name,hp,mp):
 8         self.name=name
 9         self.hp=hp
10         self.mp=mp
11 
12     #行为封装成实例方法
13     #封装的实例方法
14     def attack(self):
15         print(f"{self.name}发动一次普通攻击")
16 
17     def move(self,x,y):
18         print(f"开始移动,x的坐标为{x},y的坐标为{y}")
19 
20     #方法中也可以有返回值
21     def back_city(self,b="b"):
22         print("执行回城操作")
23         print(f"使用快捷键回城{b}")
24         return 2 #代表回城的时间2秒
25 
26     #定义类方法需要使用装饰器@classmethod
27     @classmethod
28     def abc(cls):
29         print("类方法abc")
30 
31 h1=Hero("武器大师贾克斯",888,666)
32 
33 #类属性的使用
34 #通过对象.类属性名
35 print(h1.num)
36 #类也可以使用类属性:类名.类属性名
37 print(Hero.num)
38 #类不能直接调用实例属性和实例方法
39 # print(Hero.name)
40 # Hero.attack()
41 
42 #类方法的使用:
43 #通过对象名.类方法名()
44 h1.abc()
45 #通过类名.类方法名()
46 Hero.abc()

1.6.3 静态方法

什么都不传递

class a:
    @staticmethod
    def b():
         pass

他们的区别:就是是否会自动传递参数,以及传递的值是什么

 1 class Hero:
 2     #封装类属性:类中定义的变量就是类属性
 3     num=777 #num就是类属性
 4 
 5     #魔法方法,初始化实例属性,加载类时自动调用
 6     #封装的实例属性
 7     def __init__(self,name,hp,mp):
 8         self.name=name
 9         self.hp=hp
10         self.mp=mp
11 
12     #定义静态方法需要使用装饰器 @staticmethod
13     @staticmethod
14     def sendMessage():#静态方法没有默认必带参数,跟函数相似
15         print("给队友发信息")
16 
17 h1=Hero("武器大师贾克斯",888,666)
18 #静态方法:对象和类都可以使用
19 h1.sendMessage()
20 Hero.sendMessage()

如果需要接收额外的参数,可以自由的定义

总结:

  • 函数:定义在模块中,能实现功能
  • 实例方法:定义在类中的函数,而且第一个形参必须是self,代表对象本身
  • 类方法:定义在类中的函数,而且第一个形参必须是cls,代表类本身,使用@classmethod装饰器装饰
  • 静态方法:定义在类中的函数,而且没有固定的必带形参,使用@staticmethod装饰器装饰
  • 魔法方法:类中已经定义的函数,以双下划线开头和结尾,不需要手动调用,在某种条件下自动触发
  • 共同点
    • 都是通过def关键字进行定义,所有函数和方法的形参及实参的传递都符合函数的基本类型使用
    • 位置参数
    • 默认参数
    • 关键字参数
    • 不定长位置参数
    • 不定长关键字参数
    • 都可以使用return

1.7 面向对象三大特征

1.7.1 封装

封装:对数据进行封装,防止被外部意外的修改或者破坏

Python约定:

  • 下划线开头的(属性和方法),仅供类内部使用
  • 双下线开头的(属性和方法),仅供本类使用

约定没有强制约束力,可以绕过

  1. b._age=-100 #age属性:可以绕过
  2. b._A__age=-100 #age属性:可以绕过
class A:
    name=""
    _age=0
    __sex='男'

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self,value):
        assert  value>0
        self._age=value

    def say(self):
        print(f"I am {self.name},{self._age} year old")

a=A()
a.age=1 #age属性:可读不可写

print(a.age)
print(a.say())

class B(A):
    def b_say(self):
        print(f"I am {self.name},{self._age} years old")

b=B()
b.age=3
b._age=-100 #age属性:可以绕过
b._A__age=-100 #age属性:可以绕过

print(b.age)

1.7.2 继承

首先,继承实现了代码的复制

其次,继承创建了关系:子类的实例,也被认为是父类的实例

1.7.2.1 单继承

class A:
    name=""
    _age=0
    __sex='男'

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self,value):
        assert  value>0
        self._age=value

    def say(self):
        print(f"I am {self.name},{self._age} year old")

a=A()
a.age=1 #age属性:可读不可写
print(a.age)
print(a.say())

class B(A):
    def b_say(self):
        print(f"I am {self.name},{self._age} years old")

b=B()
b.age=3
print(b.age)

print("b是B的实例对象",isinstance(b,B))
print("b是A的实例对象",isinstance(b,A))

例如:

 1 class A:
 2     #定义类属性a
 3     a=6
 4     def __init__(self):
 5         #定义实例属性b
 6         self.b=7
 7 
 8     def q(self):
 9         print("调用了实例方法q")
10 
11     @classmethod
12     def w(cls):
13         print("调用了类方法w")
14 
15 class B(A):
16     pass
17 
18 #继承:定义类的时候,类名(父类名)
19 #B类继承了A类的所有属性和方法
20 #通过B类创建对象b1
21 b1=B()
22 print(b1.a)
23 print(b1.b)
24 b1.q()
25 b1.w()
26 
27 print(b1.q())#None,方法如果没有使用return返回值,那么方法调用完打印就是None空值

1.7.2.2 多继承

 1 class A:
 2     #定义类属性a
 3     a=6
 4     def __init__(self):
 5         #定义实例属性b
 6         self.b=7
 7 
 8     def q(self):
 9         print("调用了A类的实例方法q")
10 
11     @classmethod
12     def w(cls):
13         print("调用了A类的类方法w")
14 
15 class B:
16     def e(self):
17         print("这是B类实例方法e")
18     def r(self):
19         print("这是B类实例方法r")
20 
21 #使用多继承,只需要在括号中写多个父类的名字即可
22 class C(A,B):
23     pass
24 
25 c1=C()
26 c1.w()
27 c1.q()
28 c1.e()
29 c1.r()

如果父类中的方法或者属性重名,优先使用离他近的类的方法

 1 class A:
 2     def e(self):
 3         print("调用了A类的实例方法e")
 4 
 5 class B:
 6     def e(self):
 7         print("这是B类实例方法e")
 8 
 9 
10 #使用多继承,只需要在括号中写多个父类的名字即可
11 class C(A,B):
12     pass
13 
14 c1=C()
15 c1.e() #调用了A类的实例方法e:优先继承A类
16 
17 #使用魔法属性__mro__或方法mro()查看继承的先后顺序
18 #[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
19 print(C.mro())
20 #(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
21 print(C.__mro__)

如果子类中和父类中的方法重名。优先使用子类的方法

 

1.7.3 多态

B并非一定是A的子类,但是如果B在用起来和A没有区别,那么B也可以代替A出席需要A的场合

任意一个类型,都可以代替目标类型出现

在使用的时候,数据能够按照期望的方式使用,就可以了

class A:
    name=""
    _age=0
    __sex='男'

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self,value):
        assert  value>0
        self._age=value

    def say(self):
        print(f"I am {self.name},{self._age} year old")

class B(A):
    def b_say(self):
        print(f"I am {self.name},{self._age} years old")

print("b是B的实例对象",isinstance(b,B))
print("b是A的实例对象",isinstance(b,A))

def hello(obj:A): #hello 函数需要A的实例参数作为参数
    obj.say() #因为要调用say方法

hello(b)

 1.8 数据类装饰器

可以使用对类的装饰器进行数据类的装饰

使用及特点:

  • 首先需要导入dataclasses模块
  • 使用dataclass装饰器
  • 自动为类创建很多特殊方法,包括init魔法方法
  • 不需要手动定义init魔法方法就可以直接使用
  • 实例属性的另外一种定义格式
 1 import dataclasses
 2 
 3 @dataclasses.dataclass
 4 class Person:
 5     #通过数据类定义实例属性一定要写数据类型的申明
 6     name:str="张三"
 7     age:int=18
 8     def playGame(self):
 9         if self.age>=18:
10             print(f"{self.name}可以进网吧玩游戏")
11         else:
12             print(f"{self.name}你是未成年,不能进网吧玩游戏")
13 
14 p=Person()
15 print(p.name)
16 print(p.age)
17 p.playGame()

1.9 练习

#1.通过类 设计一种动物,他们应该拥有不同的名字、体重、身高,每个动物都可以计算出自己的BMI

class Animal:
    name=""
    weight=0
    height=0

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

    def bmi(self):
        """
        bmi=体重/(身高*身高 )
        :return:
        """
        return self.weight/(self.height*self.height)

a=Animal("金毛",200,500)

b=Animal("小喵",80,150)

#2.根据面向对象的特性,创建一个可以被say函数作为参数的Cat类,使一下代码执行正确

class Dog:
    _msg="旺旺"

    def say(self):
        return  self._msg

# class Cat(Dog): #方式1:通过继承实现
#     _msg="喵喵"

class Cat:
    def say(self): #方式2:通过多态实现
        return "喵喵"

def say(obj):
    print(obj.say())

say(Dog())

say(Cat())

  

posted @ 2023-06-07 10:32  万溪汇海  阅读(5)  评论(0编辑  收藏  举报