Python面向对象

OOP

类与对象是面向对象编程的两个主要方面。
一个类(Class)能够创建一种新的类型(Type)
其中对象(Object)就是类的实例(Instance)

规范一些称呼

  • 类或对象中的变量称作字段
    • 字段有两种
      • 实例变量(对象私有)
      • 类变量(所有对象共享一份 可以通过类来调用)
  • 类中的函数称作方法
  • 字段和方法都是所属类的属性

定义类

定义一个类的格式

# 类名的首字母要大写
class 类名(object):     # 最顶层的类写(object) 也可以不写
    def method1(self):  # 这个self必须写
        pass
----------------------------------------------------------
class A:    # 定义一个类
    pass


a = A()     # 实例化一个类的对象
print(A)        # <class '__main__.A'>
print(a)        # <__main__.A object at 0x000002B312C6D128>

类中可以定义若干个变量,以及定义类中的方法


在python2中有两种类 经典类/新式类 py3中的统一都是新式类
py2中没有继承object的话 该类就是一个经典类
要声明新式类需要继承object

对象

python中实例化对象

# python是不需要new的 直接类()
变量 = 类(参数)

# 变量接收到一个新创建的类的实例
# 然后通过变量通过.来调用对象中的实例属性(实例方法和实例变量)

在类中,普通方法的参数列表中都要有一个self,这个self表示对象本身
如果想调用其他实例属性的话,需要在标识符前面加上self.

class A(object):
    a = 1
    b = 2

    def __init__(self, a=1, b=2):
        self.a = a
        self.b = b

    def ab(self):
        print(self.a + self.b)

    def printab(self):
        self.ab()

ab = A()
ab.printab()

个人看法:方法是从设计层面
函数是程序运行,过程式的一种称为

可以大概理解为 类是一种生产饼干的配方,而制作出来的饼干是对象
有了配方可以产生很多饼干
加入不同的调料可以产生不同类型的饼干

通过传入不同的值来产生不同的对象,而且每实例化一次类就会生成一个对象

ab3 = A()
ab1 = A()
ab2 = A()

print(hex(id(ab1)))
print(hex(id(ab2)))
print(hex(id(ab3)))

# 输出结果
0x257f3713400
0x257f370b0f0
0x257f3514a90

字段

就是类中的变量

类变量的增删改查

class A:
    method = 20    # 在其他语言中 就是带static的静态变量

print(A.method1)    #查看一个类属性
A.value1 = 100      #给一个类属性重新赋值
del A.value     #删除类属性
A.value2 = 200      #增加新的类属性

也可以通过类.__dict__来进行修改

实例变量和类变量的操作方式几乎一样
在类中定义为self.xxx = value的就是实力变量

这个self其实可以换成其他名

方法

init

__init__ 构造方法
作用:初始化对象的成员属性

以前用java初始化成员属性的时候

int a;
类(int a){
    this.a = a;
}

# 而静态变量就是可以直接通过类来调用的"类变量"

static b = 2;

在python中

b = 2       # 类变量 可以直接通过类来访问
def __init__(self,a):
    self.a=a    # 实例属性

实例化对象的时候,在类()传参,就是在给__init__()进行传参
还有__init__()必须返回None 也就是不写return

类中 定义方法 和在类的外面定义函数是一样的,不过类中的方法需要写一个默认的self参数作为对象本身

@property

class A:
    @property
    def a123(self):
        print(123)

a = A()
a.a123      # 被@property修饰的方法可以被当做变量来调用

类方法与静态方法

类方法只需要通过类来调用方法

# 格式

# 类方法
@classmethod
def A(cls):
    pass

# 静态方法
@staticmethod
def B()
    pass

类方法要加上装饰器@classmethod
类方法的()里要传一个参数cls 这个形参接收的是一个类名

@classmethod
def c(cls):
    print(123)

类方法就是用来操作类属性的,和对象无关的
尽量不要通过对象来调用类方法

还有静态方法

@staticmethod
def d():
    print(456)

调用的话直接通过类调用

python的类中可以有三种方法

  • 普通方法(self,…) 和对象绑定 不需要装饰器 由对象调用 可以使用成员属性 类名.类属性
  • 类方法(cls,…) 和类绑定 @classmethod 由类调用 不能使用成员属性 cls.类属性
  • 静态方法(…) 类的工具包 @staticmethod 由类调用 不能使用成员属性 类名.类属性
class A:
    a123 = 1

    @classmethod
    def a123(cls):
        print(cls.a123)

    @staticmethod
    def a123_n():
        print(A.a123)

    def a123_n2(self):
        print(A.a123)

类的组合

类和类之间的关联

class gn_a:

    def m1(self):
        print(123)


class gn_b:
    def m1(self):
        print(456)


class gn_c:
    def m1(self):
        print(678)


class gn:
    def __init__(self, a, b, c):
        # 将其他类的实例化对象赋值给主类的实例变量
        # 然后在类中可以通过实例变量来调用相应对象中的属性
        self.gn_a = a()
        self.gn_b = b()
        self.gn_c = c()

    def m1(self):
        self.gn_a.m1()
        self.gn_b.m1()
        self.gn_c.m1()


gn = gn(gn_a, gn_b, gn_c)
gn.m1()

OOP特性

继承

A类继承了B类,A类就可以使用B类的方法 ,A类的实例化对象可以调用B类的方法
继承有单继承和多继承 (在Java C#中是不允许多继承)

python是允许多继承的

class 父类2:
    pass
class 父类1:
    pass
class 子类(父类1,父类2):
    pass

子类可以通过super来调用父类的属性/方法

class B(A):

    def __init__(self, a=1, b=2, c=3):
        super().__init__(a, b, c)

    def ab(self):
        super().ab()

    def printab(self):
        super().printab()

子类中出现和父类中某个方法同名的情况下,子类实例对象优先调用子类的方法

什么时候用继承,什么时候用组合

(1)多个子类具有相同的功能,可以把这些功能提取到基类中,然后子类们继承这个基类,这个时候就会用到继承
(2)将一个类拆成多个模块和一个主体,主体的字段接收到模块类的实例化对象,来通过字段来调用模块类的方法,这个时候就会用到组合
python的接口继承

python中的接口继承就是 父类只定义方法名和属性,不做具体的实现
子类继承父类然后重写父类的方法
默认情况下,在python中子类也可以不去重写父类的方法(也就是说父类没有对子类产生限制作用,而且使用alt+ins 也无法选择implement methods选项[这个选项在其他语言中就是实现接口方法的])
不过python中有一个abc的模块…

import abc
class A(ametaclass=abc.ABCMeta):

    @abc.abstractmethod
    def m1(self):
        pass

    @abc.abstractmethod
    def m2(self):
        pass

    @abc.abstractmethod
    def m3(self):
        pass

    @abc.abstractmethod
    def m4(self):
        pass

class B(A):
    # alt+ins 选择的implement methods 然后选择所有的父类接口的方法
    # 继承了接口类之后 如果没有重写所有的父类方法的话,当前类是无法进行实例化的
    def m1(self):
        pass

    def m2(self):
        pass

    def m3(self):
        pass

    def m4(self):
        pass
python中的继承顺序

python是可以多继承的 而Java C#只能单继承

python类如果继承了多个类
- 经典类多继承会遵循深度优先
- 新式类多继承会遵循广度优先

class AA:
    pass

class A1(AA):
    pass


class B1(AA):
    pass


class A2(A1):
    pass


class B2(B1):
    pass


class C(A2, B2):
    pass


# 如果是经典类的话 深度优先 C->A2->A1->AA 然后再找B2->B1  先一条路找到头 找不到的话换一条路
# 如果是新式类的话 广度优先 C->A2->B1 然后再找B2->B1 最后找AA 

py3中的类都是新式类,按照广度优先找
类的继承顺序可以通过子类调用__mro__来查看

mro的构造是通过一个叫c3线性算法来实现的 这个mro遵循三条规则

  • 子类会优先于父类被检查
  • 多个父类会根据mro中的顺序被检查
  • 如果低层父类有a方法 高层父类也有a方法 子类在调用a方法的时候优先调用低层父类的a方法
# py3
(<class '__main__.C'>, 
<class '__main__.A2'>, <class '__main__.A1'>,
 <class '__main__.B2'>, <class '__main__.B1'>,
<class 'object'>)


# py2
# 最顶层的基类继承的object
(<class '__main__.C'>, 
<class '__main__.A2'>, <class '__main__.A1'>,
 <class '__main__.B2'>, <class '__main__.B1'>, 
 <type 'object'>)   # 注意这里objecttype类型而不是class

# 顶层类如果不继承object的话 子类是没有__mro__这个属性(经典类没有这个属性)
在子类中使用父类的方法
class AA:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    def m1(self, a, b, c):
        print(123)


class A1(AA):

    def __init__(self, a, b, c, d, e):
        super().__init__(a, b, c)    # 子类调用的父类的init的初始化语句 这个init连self都不用传
        # super(AA,self).__init__(a,b,c) 也可以这样写
        self.d = d
        self.e = e

    def m1(self, a, b, c):
        super().m1(a, b, c)    # 子类调用父类的m1方法
        print(self.a)
        print(self.b)
        print(self.c)
        print(self.d)
        print(self.e)


a1 = A1('a', 'b', 'c', 'd', 'e')
a1.m1(1, 2, 3) 

# 运行程序
123
a
b
c
d
e

使用super()的话可以避免使用父类名

多态

其他语言中的多态是这样

父类 vf = new 子类()
vf.f1()
vf.f2()
# 这个时候父类类型的变量 vf只能调用 父类中存在且被子类重写的该父类的方法
# 如果vf想要调用子类的特有功能,需要强转为子类对象

子类 new_vf = (子类)vf
new_vf.z1()
new_vf.z2()

而python实例化对象不需要声明类的类型
实例化一个子类的对象,可以使用所有子类中能够调用的方法

所以 python中提多态是没有什么用的

封装

就是把东西装到一个容器中,然后封上
对外只提供一个接口访问

封装的本质就是明确区分内外,真正意义上的封装是内部实现逻辑,外部提供接口

既然提到了封装 就要说一下python中的权限修饰符
这里写图片描述

普通的成员方法和成员属性,都是可以直接从外部调用的(相对于java中public修饰符来说)
python中没有public和private这样的关键字
双下划线__属性/方法 可以起到private的作用

外部是无法直接通过对象获取私有属性的
如果要给私有属性赋值的话,相当于新增了一个属性

ab1 = A()
print(ab1.__dict__)     # {'a': 1, '_A__b': 2, 'c': 3}
ab1.__b = 123
print(ab1.__dict__)     # {'a   ': 1, '_A__b': 2, 'c': 3, '__b': 123}


# 类中的私有属性 是_类__属性

严格意义上来说,python中是没有私有变量的(因为被重命名了)
在java和C#中 被private修饰的字段需要提供getXX和setXX来进行访问

在python中的话 也应该这么写

class A:
    def __init__(self):
        self.__a = 123

    def get_a(self):
        return self.__a

    def set_a(self, a):
        self.__a = a


a = A()
print(a.get_a())  # 123
a.set_a(20)
print(a.get_a())  # 20

魔术方法

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

对象.__dict__ 打印字一个字典,字典里面包含所有对象可以调用的成员属性
(包括私有的成员属性)

打印一个类的__dict__

{'__module__': '__main__', 'c': 1, '__init__': <function A.__init__ at 0x000001B449FB57B8>, 'ab': <function A.ab at 0x000001B44B0329D8>, 'printab': <function A.printab at 0x000001B44B032A60>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

对象.__class__

print(ab1.__class__)

# 打印结果
<class '__main__.A'>

在类中可以通过self.__class__来调用类方法/属性

其他

class C:
    pass
class B(C):
    pass
class A(B):
    pass


print(A.__name__)       #类的名字
print(A.__doc__)        #类的文档字符串
print(A.__bases__)      # (<class '__main__.B'>,) 所有父类构成的元组
# 出现多继承的情况 类似(<class '__main__.B'>, <class '__main__.D'>)

print(A.__dict__)   # 类A的属性
print(A.__module__) # 类A所在的模块 如果当前文件是执行文件的话 会打印__main__
print(A.__class__)  # 类作为实例对应的类  <class 'type'>

a = A()
print(a.__class__)  # <class '__main__.A'>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python面向对象编程是一种编程范式,它将程序组织成对象的集合,每个对象都有自己的属性和方法。在Python中,可以通过定义类来创建对象,并通过实例化类来创建具体的对象。引用[1]中的代码示例展示了一个Animal类,其中包含了初始化方法和一个__str__方法来返回对象的描述信息。通过这个类,可以创建Animal对象,并通过print函数输出对象。引用中的代码示例展示了如何使用@property装饰器来定义类的属性和属性的访问方法。通过这种方式,可以在访问属性时像访问普通属性一样使用点号,而不需要使用方法调用的方式。引用中的代码示例展示了多态在Python中的应用。多态是面向对象编程的重要概念,它允许不同的对象以相同的方式对外部访问,但具体的实现可能不同。在这个示例中,father、son和daughter类都继承了father类,并重写了tell方法。通过调用不同的对象的tell方法,可以看到不同的输出结果。总之,Python面向对象编程是一种灵活且强大的编程方式,它允许开发者以对象为中心来思考和组织代码,提高了代码的可读性和可维护性。通过定义类、创建对象和使用类的属性和方法,可以实现丰富多样的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Python面向对象(全套)](https://blog.csdn.net/Thewei666/article/details/126652501)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值