12.面向对象

一、类

1.类基础

1.1 概念

用来描述具有相同的属性和方法的对象的集合。

1.2 示例代码

这是一个最简单的类

class MyClass:
	"""类的帮助或者描述信息"""
    
    # 属性
    num = 1
    
    # 方法
    def test(self):
        print('这是一个方法')

1.3 查看类帮助信息

print(类名.__doc__)
或者:
print(help(类名))

2.属性和方法

2.1 属性

在类中,变量我们称为 “属性”

2.2 方法

在类中,函数我们称为"方法"。
类的方法与普通的函数只有一个特别的区别:方法必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。
self代表类的实例,而非类

3.访问类属性

print(MyClass.num)

4.访问方法

在不创建对象的情况下,无法直接通过类名调用类中的方法。因为缺少第一参数self的注入

二、对象

1.概念

1.类的实例化就是创建"对象"
2."对象"包括两个数据成员和方法:

两个数据成员是:
	1.类变量    属于类的
	2.实例变量  属于实例的

方法:
	类中定义的函数

2.创建对象

class MyClass:
	num = 1   
	def test(self):
		print('这是一个方法')

# 这里创建了一个对象
x = MyClass() 

3.对象的唯一性

这里实力化了两个对象,两个对象的地址内存地址是完全不一样的

class MyClass:
    num = 1
    def test(self):
        print("这是类中的一个方法")

m1 = MyClass()
m2 = MyClass()

m1_addrss = hex(id(m1))
m2_addres = hex(id(m2))

print(f"m1的内存地址是:{m1_addrss}")
print(f"m2的内存地址是:{m2_addres}")

结果如下

m1的内存地址是:0x28313908400
m2的内存地址是:0x283139e68b0

4.实例化过程

对象实例化过程分为两步:

1.实例化: 调用__new__  方法 实例化
2.初始化: 调用__init__ 方法 初始化

这两个方法不用手动调用,在实例化对象的时候,python解释器会自动调用这两个方法。

5.对象访问类属性

class MyClass:
    num = 1
    def test(self):
        print("这是类中的一个方法")

m1 = MyClass()
m2 = MyClass()

print(m1.num)

6.对象访问类方法

两个实例化对象可以调用类方法

class MyClass:
    num = 1
    def test(self):
        print("这是类中的一个方法")

m1 = MyClass()
m2 = MyClass()

m1.test()
m2.test()

7.self

7.1 self是谁

上边也讲解过,self是指向实例本身。也就是说,谁调用方法,self就是谁.
下边就实例证明了这一点

class MyClass:
    num = 1
    def test(self):
        print(f"self的地址是:{hex(id(self))}")

m1 = MyClass()
m2 = MyClass()

m1_add = hex(id(m1))
print(f"m1的地址是:{m1_add}")
m1.test()

print('-' * 100)

m2_add = hex(id(m2))
print(f"m1的地址是:{m2_add}")
m2.test()

结果如下:

m1的地址是:0x1b8793c8400
self的地址是:0x1b8793c8400
----------------------------------------------------------------------------------------------------
m1的地址是:0x1b8793d68b0
self的地址是:0x1b8793d68b0

这里可以看出,谁调用类方法,self就是谁。

7.2 self的自动注入

为什么slef是类方法中的必须要存在的第一参数,而用户调用的时候不用传递self的实参。这是因为:
实例在调用类方法的时候,自动将对象和方法进行了绑定,自动将对象和self做了对应。
代码如下:

class MyClass:
    num = 1
    def test(self):
        print(f"self的地址是:{hex(id(self))}")

m1 = MyClass()
m1_addr = hex(id(m1))

print(f"m1的内存地址是:{m1_addr}")
print(m1.test)

结果如下:

m1的内存地址是:0x2c0ffb18400
<bound method MyClass.test of <__main__.MyClass object at 0x000002C0FFB18400>>

8.实例属性

8.1 初始化实例属性

这是在类中增加了初始化方法,来初始化对象,已经实例属性

class MyClass:
    # 在初始胡时候直接定义属于实例自己的实例属性
    def __init__(self,name,age):
        self.name = name
        self.age = age


    def get(self):
        print(f"我的名字叫{self.name},我今年{self.age}岁了")


m1 = MyClass("zhangsan",20)

m1.get()

8.2 单独增加实例属性

class MyClass:
    def get(self):
        print(f"我的名字叫{self.name},我今年{self.age}岁了")


m1 = MyClass()

m1.name = "zhangsan"
m1.age = 20

m1.get()

9.dict方法

1.dict是查看类的组成,
2.可以查看对象中的属性有哪些

class MyClass:
    def get(self):
        print(f"我的名字叫{self.name},我今年{self.age}岁了")


m1 = MyClass()

m1.name = "zhangsan"
m1.age = 20


print(MyClass.__dict__)
print('-' * 20)
print(m1.__dict__)

结果如下:

{'__module__': '__main__', 'get': <function MyClass.get at 0x000001DE1E706430>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None}
--------------------
{'name': 'zhangsan', 'age': 20}

10.类属性和实例属性冲突

实例1:

代码实例:

class MyClass:
    name = "zhangsan"

m1 = MyClass()
m2 = MyClass()

m1.name = "lisi"
print(m1.name)
print(m2.name)

当实例属性和类属性冲突时,对象先找自己的实例属性,如果没有在找类属性。

实例2:

class MyClass:
    name = "zhangsan"

m1 = MyClass()
m2 = MyClass()

m1.name = "lisi"
print(m1.name)

# 直接通过__class__来访问类中的属性,这相当于 MyClass.name
print(m1.__class__.name)

总结:

(1)在类变量和实例变量冲突时,先找实例变量,在找类变量
​ (2)在实例变量中修改变量不会对类变量生效。
​ (3)如果实例变量是引用的类变量,修改类变量 实例变量也会随之修改

三、封装

1.私有成员

封装的主要作用:是对 对象的属性进行保护,不能让用户直接进行修改。
在属性的前边加上两个下划线,此属性或者方法就变成了私有属性或方法。

class MyClass:
    __name = "zhangsan"

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


m1 = MyClass(20)

print(m1.__name)
print(m1.__age)

调用两个变量都报错。

2.封装get方法

class MyClass:
    __name = "zhangsan"

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

    def get(self):
    	# 通过类来访问类属性
        print(f"类属性的值:{__class__.__name},实例的属性是:{self.__age}")
        # 通过实例来访问类属性
        print(f"类属性的值:{self.__name},实例的属性是:{self.__age}")

m1 = MyClass(20)

m1.get()

3.查看对象的所有属性和方法

3.1 dir函数

dir可以查看对象可以使用的属性和方法

class MyClass:
    __name = "zhangsan"

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

m1 = MyClass(20)

print(dir(m1))

3.2 python封装的原理

其实在pyton中,属性或者方法私有化之后,被python解释器改名了.以下实例通过查看对象的所有属性,可以看到私有属性改名后的名称。然后通过改名后的名称访问私有属性。当然生产中不建议这么访问,否则私有化属性和方法就没有意义了

class MyClass:
    __name = "zhangsan"
    height = 200

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

    def get(self):
        print('hello')

m1 = MyClass(20)
m1.name = "lisi"

print(m1.__dict__)
print(m1._MyClass__age)

4.property装饰器

正常来讲,当属性私有化后,应该给用户提供set和get方法,让用户来获取和修改属性。如下代码:

class MyClass:
    def __init__(self,name,age=20):
        self.__name = name
        self.__age = age
    
    def get_name(self):
        return self.__name

    def set_name(self,name):
        if name == "root":
            print("用户名不能修改为root")
        else:
            self.__name =  name
            print("修改姓名成功")

    def get_age(self):
        return  self.__age

    def set_age(self,age):
        if age > 30:
            print("岁数太大,不能继续在上学了")
        else:
            self.__age = age
            print("年龄修改成功")



m1 = MyClass("zhangsan")
print(m1.get_name())
print(m1.get_age())

m1.set_age(100)
m1.set_name("root")


以上代码是对于私有属性的标准格式。
property装饰器优化了两个地方
1.编写类方法的名称变得简洁。不用在带上set或者get名来标识这个方法得作用
2.在调用方法得时候,不用在加上(),可以向修改变量那样给函数传值。

如下:

class MyClass:
    def __init__(self,name,age=20):
        self.__name = name
        self.__age = age

    @property
    def name(self): # 这里直接以属性得名字命名,函数名字和下边得name函数并不冲突
        return self.__name

    # 相当于set方法,这里name和下边函数名尽量一致
    @name.setter
    def name(self,name):
        if name == "root":
            print("用户名不能修改为root")
        else:
            self.__name =  name
            print("修改姓名成功")


m1 = MyClass("zhangsan")

四、继承

1.概念

继承就是,子类可以从父类那里继承过来父类得属性和方法.
继承又分为单继承和多继承

2.继承实例

class father:
    num = 10000

    def showme(self):
        print("我是千万富翁")


class son(father):
    pass

s1 = son()

print(s1.num)
s1.showme()

从上述代码可以看出,子类当中,没有任何属性和方法,但是可以使用父类的方法和属性。这说明方法和属性都是继承下来的。

3.复写

对于继承下来的方法和属性不能满足当前的需求,可以进行复写.
复写只是重新定义了子类中和父类同名的方法,但是父类中的方法不会改变

class father:
    num = 10000

    def showme(self):
        print("我是千万富翁")


class son(father):
    def showme(self):
        print("我是亿万富翁")

s1 = son()

print(s1.num)
s1.showme()

4.调用父类中的同名方法

在复写父类方法之后,如何调用父类中的同名方法,使用super():代码如下

class father:
    num = 10000

    def showme(self):
        print("我是千万富翁")


class son(father):
    def showme(self):
        print("我是亿万富翁")
        super().showme()

s1 = son()

print(s1.num)
s1.showme()

五、魔术方法

在python中,前后被上下划线包围的都叫做"魔术方法"

1.str方法

触发时机:打印对象时会默认触发
一个类是实例化成对象后,打印对象,默认会打出出对象的地址。代码如下:

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

s1 = student("zhangsan",20)

print(f"s1的打印结果是: {s1}")

结果如下:

s1的打印结果是: <__main__.student object at 0x000001BEE5838400>

有些场景,对象的内存地址对于用户来说没有什么用处。此时就可以改变对象的默认的值不在是内存地址,可以返回自定义的字符串内容。代码如下:

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

    def __str__(self):
        return f"name:{self.name},age:{self.age}"

    
s1 = student("zhangsan",20)

print(f"s1的打印结果是: {s1}")

2.len方法

当对对象使用len方法时,会自动触发

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

    def __len__(self):
        return len(self.__dict__)

    def showme(self):
        print("this is student class")


s1 = student("zhangsan",20)

print(f"s1的打印结果是: {len(s1)}")

3.eq方法

判断两个对象是否相等时候自动调用

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

    def __eq__(self, other):
        if self.name == other.name:
            return "是同一个人"
        else:
            return "不是同一个"


s1 = student("zhangsan",20)
s2 = student("lisi",21)

print(s1 == s2)

魔术方法有很多,这里不做全记录了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值