07-Python之面向对象编程(类的基本操作)

时间:2021-08-15 17:22:27

# 1.实例化一个类:

- 基本格式:变量 = 类名() # 实列化一个对象

  * 案例1

# 定义了一个Person类
class Person:   
    count = 1
    age = 18
    
# 实例化对象    
# 执行class时就会得到类对象,并将其赋值给变量名(Person)
p = Person()    # p是一个变量,指向了Person类产生的对象
print(Person)  # Person是一个类
print(p)       # p就成了Person类产生的对象 
"""
# 结果
<class '__main__.Person'>
<__main__.Person object at 0x0000022294ED7F98>
"""

# 2.查看类的信息

- 2.1.可以通过默认内置变量来检查类和对象的所有成员
  - 对象所有成员检查:obj.__dict__
  - 类所有成员检查:class_name.__dict__
- 2.2.使用help()函数
- 2.3.使用dir()函数
- 2.4.使用type()查看实例的类型归属
- 2.5.使用内置方法isinstance(实例, ClassName)来测试一个对象是否为某各类的实例

  * 案例2

# 定义了一个Person类
class Person:   
    name = "China"
    age = 18
    
# 实例化对象     
p = Person()    

# 1.查看成员
    # 1.1查看对象的成员
print(p.__dict__)        # 空字典
    # 1.2查看类的成员
print(Person.__dict__)

# 2.使用help()函数查看
print(help(p))     
# print(help(Person))  # 两个会是一样的结果,指向关系

# 3.使用dir()可以查看对象的属性和方法
print(dir(Person))   # print(dir(p))效果一样

# 4.查看归属
print(type(p), type(Person)) # <class '__main__.Person'> <class 'type'>

# 5.判断关系
print(isinstance(p,Person))
    # 如果是该类的实例返回True,否则返回False
    # 注意要判断的p和类Person一定要存在,不然会报错。

# 3.对类的属性进行操作

- 3.0.属性和变量区别:
  - 变量:根据不同的位置,有不同的访问权限
  - 属性:只能通过变量名来引用。

- 3.1.访问对象成员
  - 使用点操作符
  - obj.成员属性名称
  - obj.成员方法

- 3.2.增加属性
  - 动态增加增加
    变量.属性名 = 值
  - 实例对象可以通过字典__dict__进行增加和修改
  - 通过类的初始化方法(构造方法)
    __init__ 方法

- 3.3.删除属性
  - 一般情况下,属性存储在__dict__的字典当中
    - 有些内置对象没有这个__dict__ 属性
  - 一般对象可以直接修改__dict__属性
  - 但类对象的__dict__为只读模式
    - 默认无法修改
    - 可以通过setarr方法修改

  * 案例3

class Person:   
    age = 18
    
p = Person()    # 实例化对象  

# 1.进行访问  
    # 1.1通过类名进行访问
print(Person.age)  # 18
    # 1.2通过实例化对象进行访问   
print(p.age)       # 18            
"""-----------------------------------------------------------"""
# 2.增加操作
    # 注意:如果要增加的变量已存在,则修改
    # 2.1动态增加
        # 2.1.1直接通过类名进行属性增加
Person.name = 'abc'   
print("Person:", Person.__dict__)   
print("p:", p.__dict__)   # 空字典
        # 2.1.2通过实例进行增加
p.zeng = 100    # 只在p里面增加,不影响Person
print("Person:", Person.__dict__)
print("p:", p.__dict__)    # {'zeng': 100}
    # 2.2使用构造方法(属于方法的操作)
"""-----------------------------------------------------------"""
# 3.删除操作
    # 注意:
        # del p.age  #会报错,p内不存在age
        # 可以进行del p.zeng,因为前面增加了zeng属性
    # 3.1实例删除
del p.zeng
    # 3.2直接通过类
del Person.age
print("Person:", Person.__dict__)    # 缺少了age属性
print("p:", p.__dict__)   # 空字典

# 4.类的属性的存储问题

- 4.1一般情况下,属性存储在__dict__的字典当中。
  - 实例对象一般可以直接修改__dict__属性
  - 类对象的__dict__为只读
- 4.2实例对象和类对象是存储在不同一块空间。
- 4.3访问时:
  - 通过类对象:直接在类对象中查找
  - 通过实例对象:优先在实例对象内部查找,没有找到需要的,就到指向的类对象中查找。
- 4.4修改时的区别:
  - 通过实例对象进行增加、删除或者修改属性的值,都是在实例对象内部进行,对类对象没有影响
  - 通过类对象进行增加、删除属性时:
    - 对实例化对象内部没有的属性产生影响。
    - 对实例化对象内部已拥有相同的属性不产生影响。

  * 案例4.1

# 1.一般情况下,属性存储在__dict__的字典当中
class Person():
    age = 18
    
p = Person()
print(p.__dict__)  # 空字典{}
print(p.age)# 18 
            # 虽然是空字典但是能访问Person的属性,指向的关系
"""-----------------------------------------------------------"""    
# 1.1可以通过字典__dict__修改实例对象的属性
p.__dict__ = {"count":2,"sex":""}
"""-----------------------------------------------------------"""
# 1.2类对象的__dict__为只读
#Person.__dict__ = {"count":3,"age":15}
#Person.__dict__ = {"sex":"男"}  #会报错,
"""-----------------------------------------------------------"""
print(Person.__dict__)   
print(p.__dict__)   # {'count': 2, 'sex': '男'}

  * 案例4.2

# 2.实例对象和类对象是存储在不同一块空间。
class Person():
    age = 19
    pass
"""-----------------------------------------------------------"""
# 2.1内存上开辟了新空间,两者的地址不一样
p = Person()  
print(id(p), id(Person))
"""-----------------------------------------------------------"""
# 2.2修改和新增类的地址都不变
p.age = 18  
Person.name = "aaaa"
print(id(p), id(Person))
"""-----------------------------------------------------------"""
# 2.3属性修改后的地址变化和列表的修改变化一样
p.pets = ["car","dog"]
print(p.pets,id(p.pets)) 

p.pets = ["car", "dog", "fish"]  # 直接修改   
print(p.pets,id(p.pets))
p.pets.append("iiiii")    # 进行追加操作
print(p.pets,id(p.pets))
"""-----------------------------------------------------------"""
print(p.__dict__)  #{'age': 18, 'pets': ['car', 'dog', 'fish', 'iiiii']}
print(Person.__dict__)  # Person 中z属性没发生改变

  * 案例4.3

# 3.访问时:
class Person():
    age = 19
    pass
p = Person()  
p.age = 18
print(p.age)    # 18  # 优先访问自己的内部属性
print(Person.age) # 19 # Person.age没有被修改,
                    # 通过类访问,直接指向类对象的age

  * 案例4.4

# 4.修改时:
class Person():
    age = 18
    pass
p = Person()  
# 4.1未修改前类对象和实例对象指向同一个属性。
print("Person:",Person.age, id(Person.age))
print("p     :", p.age, id(p.age))
print("---"*10)

# 4.2通过实例对象进行修改,没有对类对象产生影响
p.age = 28
print("Person:",Person.age, id(Person.age))
print("p     :", p.age, id(p.age))   
print("---"*10)

# 4.3通过类对象进行修改
Person.age = 38
    # 4.3.1实例对象p内部有age属性,无法造成影响
print("Person:",Person.age, id(Person.age))
print("p     :", p.age, id(p.age)) 
print("---"*10)

    # 4.3.2实例对象p内部没有age属性,无法造成影响
del p.age   # 删除p中的age
print("p     :", p.age, id(p.age))   # 此时访问p.age则指向Person.age
Person.age = 48    # 通过类对象修改age
print("Person:",Person.age, id(Person.age))
print("p     :", p.age, id(p.age)) 

# 5.对类的方法进行操作

- 5.1方法的存储:方法存储在类里面,不管是哪一种类型的方法,都是存储在类当中,没有在实例当中

  * 案例5.1

class Person():
    
    # 1.实例方法:默认第一个参数需要接受到一个实例
    def eat(self):    # 必须要有参数,默认是self
        print("这是一个实例方法",self)
    
    # 2.类方法:默认第一个参数需要接受到一个实例
    @classmethod
    def leifangfa(cls):
        print("这是一个类方法",cls)

    # 33.静态方法:第一个参数啥也不默认接受
    @staticmethod
    def jingtaifangfa():
        print("这是一个静态方法")
        
p = Person()
# 查看参数
print(p)
p.eat()   # self和p的地址一样
Person.leifangfa()   # <__main__.Person object at 0x0000022294ECFE48>
Person.jingtaifangfa()  # <class '__main__.Person'>
# 查看方法存储
print(p.__dict__)   # 空字典{}
print(Person.__dict__)  

-  5.2实例方法调用方式:本质就是找到函数本身调用
  - 标准调用(推荐使用):使用实例调用
  - 类调用: 使用类名进行调用,没有意义
  - 间接调用

  * 案例5.2

class Person:
    def eat(self,food):
        print("我在吃饭",self, food)
        
# 1.标准调用
p = Person()
print(p) # p就时self,系统自动传入
print(p.eat("rich"))  # 这是实例化 可以只写一个参数,self是默认参数,可以不用写
                        # 标准调用,实例化调用 不用写第一个参数self        
    
# 2.类调用   
print(Person.eat)   # Person.eat 是一个函数
print(Person.eat(555,"FISH"))# 必须要有两个参数,不然会报错
                                # self = 555, food = FISH
# 3.间接调用
func = Person.eat  # Person.eat 是一个函数,func指向Person.eat
print(func(123,"DDD"))     # self = 123, food = DDD

- 5.3类方法

  * 案例5.3

class Person():
    @classmethod    # 通过这个装饰器
    def leifangfa(cls, a):
        print("这是一个类方法",cls, a)

# 1.类调用
print(Person.leifangfa(123))

# 2.间接调用
func = Person.leifangfa
print(func(456))

# 3.实例化,标准调用
p = Person()
print(p.leifangfa(789))

- 5.4静态方法

  * 案例5.4

class Person():
    @staticmethod
    def leifangfa(self):    # 参数没有默认接受
        print("这是一个静态方法",self)

# 1.类调用
print(Person.leifangfa(1))

# 2.间接调用
func = Person.leifangfa
print(func(2))

# 3.实例化,标准调用
p = Person()
print(p.leifangfa(3))

- 5.4不同方法的调用

  * 案例5.5

class Person():
    age = 18  # 能通过实例和类访问
    def shilifangfa(self):   
        print(self)
        print(self.age)
        print(self.num)
        
    @classmethod
    def leifangfa(cls):
        print(cls)
        print(cls.age)
       #  print(Person.num)  无法访问

    @staticmethod
    def jingtaifangfa():
        print("jingtai")
p = Person()
p.num = 10  # 只能通过实例访问,无法通过类访问: Person.num
# 调用类属性
print(Person.age)  # 18
print(p.age)  # 18
# 调用实例属性
print(p.num) # 19
print('--'*20)
print(p.shilifangfa())
print('--'*20)
print(p.leifangfa())
print('--'*20)
print(p.jingtaifangfa())
"""
# 结果
18
18
10
----------------------------------------
<__main__.Person object at 0x000001AA7705B668>
18
10
None
----------------------------------------
<class '__main__.Person'>
18
None
----------------------------------------
jingtai
None
"""

# 6.相关的操作

-  6.1构造器:由__new__()和__init__()组成

  - 对象实例化的时候首先调用方法__new__()创建对象

    - 第一个参数不是self而是这个类(cls),其他参数直接传递个__init__()。

    - 需要返回一个实例对象,通常是cls这个类实例化的对象。

    - 如果用户不提供自己的__new__(),Python自动调用object.__new__()方法。

  - 之后调用__init__()对其进行初始化

    - __init__()一般用来为数据成员设置初值或进行其他初始化工作

    - 在创建对象时被自动调用和执行。

    - 如果用户没有设计构造函数,Python会自动提供一个默认的构造函数

    - 返回值一定时None

  * 案例 6.1.1

# 构造器的实践
class Person(str):
    def __new__(cls, string):
        string = string.upper()
        print("__new__")  
        print(cls)  # 第一个参数是这个类
        return str.__new__(cls, string)
        
    def __init__(self, v):
        print("__init__")
#         print(v)  # i love chian
        
a = Person("i love chian")
# 1.实例化后先执行__new__(),打印__new__
# 2.接着执行__init__(),打印__init__
print(a)    # 打印返回的类实例化的对象

  * 案例6.1.2

# 构造函数式
class Person():
    def __init__(self, n, a):
        self.name = n
        self.age = a
        
p1 = Person("sx",19)
print(p1.__dict__)  # {'name': 'sx', 'age': 19}
# print(Person.age)  # 不能访问__init__的内容
print(p1.name,p1.age)  # sx 19

- 6.2析构器:由__del__()函数构成

  - 一般用来释放对象占用资源或收回对象空间时被自动调用和自行。

  - 如果用户没有设计析构函数,Python会自动提供一个默认的析构函数进行必要的清理工作。

  * 案例6.2

# 构造函数式
class Person():
    def __init__(self, n, a):
        self.name = n
        self.age = a
        print("构造函数执行完毕")
    def __del__(self):
        print(self)   
        print("对象被清除了")
        
p1 = Person("sx",19)
del p1
"""
# 结果
构造函数执行完毕
<__main__.Person object at 0x000001333CEF0EB8>
对象被清除了
"""