ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

面向对象进阶-封装,继承,多态三大特性和魔术方法

2022-04-24 23:32:34  阅读:139  来源: 互联网

标签:__ .__ 进阶 self 多态 def print 三大 name


一 三大特性

  定义 基本使用 示例
封装 

基于类与对象的语法结构,把代表数据的变量和操作数据的函数进行封装成一个类或对象,通过类与对象语法对外公开少部分的数据操作

实现对类或对象的成员保护与访问机制避免外界随意修改删除破坏类的内部数据的合理性

 

访问权限:

Python对成员的保护等级只有两个:私有的(private)(——属性/方法)和公有的(public)

class Person(object):
def __init__(self, name, money, age, password, num):
self.name = name # 公有属性
self.__num = num
self.__money = money # 私有属性
self.__password = password # 私有属性

def __test1(self):#私有方法
return 123

def get_money(self, password):
if self.__check_password(password):
# 通过在return在类的内部提供操作数据的接口方法给外界去获取、修改数据
return self.__money
else:
raise Exception("密码不正确!没钱!")

def save_money(self, money, password):
"""存钱或者取钱"""
if not self.__check_password(password):
return "密码不正确!无法操作!"

if money > 0:
# 存钱
self.__money += money
return

# 取钱
if abs(money) > self.__money:
return "余额不足!"

self.__money += money

def __check_password(self, password):
"""检测密码,私有方法"""
return password == self.__password

xiaoming = Person("小明", 1000, 17, "123456", 1)
# 类的外界,可以访问公有属性,无法访问私有属性
print(xiaoming.name) #小明
"""封装其实就是为了让一些属性和方法,不能被外界直接修改和调用而已"""
# print(xiaoming.__test1) # 报错!无法访问私有方法
# print(xiaoming.__num) # 报错! 无法访问私有属性 'Person' object has no attribute '__num'
"""对数据的修改,可以通过提供公有方法的方式给外界操作,那么外界操作时,我们就可以设置一些判断"""
print(xiaoming.get_money("123456")) #1000
xiaoming.save_money(-150, "123456")
print(xiaoming.get_money("123456")) #850
属性封装  
import hashlib
class Person(object):
def __init__(self, name, money, password):
self.name = name
self.__money = money
self.__password = self.__pwd(password)

@property #装饰器装饰接口
def money(self): # money方法因为上面加了@perperty,所以变成了只读属性方法,实际使用时,直接当成属性来进行赋值操作即可
return self.__money

@money.setter #装饰器的调用
def money(self, money):
if money < 0:
print("无法修改!")
return
self.__money = money

# 对象属性的存取器,@property 只读器, @属性名.setter 只写器
@property
def password(self):
return self.__password

@password.setter
def password(self, new_password):
self.__password = self.__pwd(new_password)

def __pwd(self, raw_password):
hash = hashlib.sha256()
hash.update(raw_password.encode())
return hash.hexdigest()

xiaoming = Person("小明", 1010, "123456")
print(xiaoming.money) #1010
print(xiaoming.password) #8d969eef6ecad3c2..
xiaoming.password = "123123123"
print(xiaoming.password) #932f3c1b56257c..
xiaoming.money = 100
print(xiaoming.money) #100

xiaohong = Person("小红", 2000, "123456")
print(xiaohong.money) #2000

xiaohong.money = -1000 #无法修改!
print(xiaohong.money) #2000 得到的结果还是原来的
继承 使用已有父类作为基础创建子类的一种方式。子类的定义可以增加新的属性或方法,也可以复用或覆盖父类已有的属性和方法 

1、子类只能继承父类的公有属性和方法,无法继承父类的私有属性和私有方法。

2、子类可以拥有父类所没有的属性和方法,即子类可以对父类进行扩展。

3、子类可以用自己的方式重写父类的方法。

 

基本使用:(单继承)

class Person(object):
"""人类"""
def __init__(self, name, age):
self.name=name
self.age=age

def eat(self):
print("eating...")
def sleep(self):
print("sleep...")

class Student(Person):
"""学生类"""
def __init__(self, name, age, achievement):
#super就是一个内置函数,用于调用父类的属性或方法,一般写法:super(子类名, self).方法(参数列表)
super().__init__(name, age)
self.achievement = achievement

"""继承不仅可以复用父类的方法,也可以复用父类的属性,甚至增加属于自己的属性。"""
def do_homework(self):
print("do_homework.....")
def do_read(self):
print("reading........")

'''继承过程中,子类不仅可以复用父类的方法,还可以重写父类的方法。'''
def sleep(self, where):
if where != "课堂":
super().sleep()
else:
print("不能睡觉!")

class Teacher(Person):
"""老师类"""
def __init__(self, name, age, salary):
super().__init__(name, age)
self.salary = salary

def teaching(self):
print("teaching.....")
s = Student("小明", 17, 100)
s.sleep("课堂")

多继承:
"""多继承,就是一个类同时继承多个父类的特性"""
class Animal(object):
def eat(self):
print("eating...")

def sleep(self):
print("sleep...")

class Fly(object):
def fly(self):
print("fly...")

class Eagle(Animal, Fly):
def hunt(self):
print("hunt....")

class Plane(Fly):
def payload(self):
print("运输....")
菱形继承:
class Human(object):
id = 1
def eat(self):
print("1-饿了去打猎")
print(self.id)
print("2-抓到猎物,直接生吃!")

class Man(Human):
id = 2
def eat(self):
print("3-饿了去买酒卖肉")
print(super().id)
print("4-一边喝酒,一边吃肉!")

class Woman(Human):
id = 3
def eat(self):
print("5-饿了去煮饭买菜")
print(super().id)
print("6-动作优雅,小口尝试!")

class Children(Man, Woman):
id = 4
def eat(self):
print("7-饿了就哭.")
print(super().id) #2
print("8-吃了接着哭。")

xm = Children()
xm.eat()
print(Children.__mro__)
#(<class '__main__.Children'>, <class '__main__.Man'>, <class '__main__.Woman'>, <class '__main__.Human'>, <class 'object'>)
多态

   弱类型语言:

一个变量被定义类型,该变量可以根据环境变化自动进行转换,不需要经过显性强制转换。


1 继承以后子类重写父类方法,不同的子类对于父类的同一个方法可以有不同的实现方式。龙生九子,子子不同。
2 支持鸭子类型(DuckType)
 
class Payment(object):
def __init__(self, name, money):
self.money = money
self.name = name

def pay(self, *args, **kwargs):
pass

class AliPay(Payment):
def pay(self):
# 支付宝支付渠道
print('%s通过支付宝消费了%s元' % (self.name, self.money))

class WeChatPay(Payment):
def pay(self):
# 微信支付渠道
print('%s通过微信消费了%s元' % (self.name, self.money))

class Order(object):
def account(self, pay_obj: Payment):
print(isinstance(pay_obj, Payment))
pay_obj.pay()

class Test(object):
def pay(self):
print("测试功能")

'''继承以后子类重写父类方法'''
pay1 = WeChatPay("小红", 100)
pay2 = AliPay("小明", 200)
#class必须实例化后才能使用
order = Order()
order.account(pay1) # True 小红通过微信消费了100元
order.account(pay2) # True 小明通过支付宝消费了200元
'''支持鸭子类型'''
t = Test() #有pay函数
order.account(t) #False 测试功能
# 在所有的编程语言中,只要是弱类型的面向对象编程语言,多态都是不明显的,而是大多都支持鸭子类型。
 

二 魔术方法

在特定条件下会自动执行的方法(函数),不需要我们自己手动调用。类中所有魔术方法都是私有方法,只会在类的内部自动执行,无法被外界调用

固定方法名描述示例
__init__ 构造方法,当对象初始化会自动调用,常用于对象属性的初始化。
class Persion(object):
def __new__(cls, *args, **kwargs): #__new__ 用来制造对象
print("Person.__new__ 被调用了")
# return object.__new__(cls)
return object.__new__(cls)
# object提供的__new__是魔术静态方法,所以需要传递当前类作为参数,才能创建当前类的实例对象

def __init__(self, name, age): #__init__ 用来初始化对象
print("Person.__init__被调用了")
self.name = name
self.age = age
'''在自动执行 `__init__()`方法前,会先执行 object.`__new__`方法'''
'''`__new__`方法,在内存中开辟对象空间并返回对象提供给`__init__()'''
xiaoming = Persion("小明", 16) #Person.__new__ 被调用了 Person.__init__被调用了
# print(xiaoming.name)
# print(xiaoming.age)
__new__ 创建实例对象方法,是调用类时自动执行的第1个魔术方法,也是一个类方法。 比__init__的执行时间要早,返回值是实例对象,也就是__init__中的self。
__call__ 可调用协议方法,当对象被当成函数时,自动执行
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
# 类的内部实现了__call__方法,则表示当前类遵循了可调用协议,这个类的所有实例对象都可以被当成函数进行调用
def __call__(self, *args, **kwargs):
print("Person.__call__")
print(f"args={args}", f"kwargs={kwargs}")
return "<Person>"

xiaoming = Person("小明", 16)
# 把对象当成一个函数来调用,那么对象内部的__call__就会被调用
# 如果类内部没有声明__call__方法,则调用失败,程序报错!
xiaoming("A", "B", "C", name="xiaoming", age=17) #Person.__call__ args=('A', 'B', 'C') kwargs={'name': 'xiaoming', 'age': 17}

# 可以通过callable内置函数来判断对象的类的内部是否实现了__call__方法
# 当然,callable也可以用于判断一个变量是否是函数或对象/类方法
print( callable(xiaoming) ) # True

# 因为Python中万物皆为对象,函数本身也是一个对象,创建函数的类是function,也在内部实现了__call__方法
print(type(函数名)) # <class 'function'>

# 可以通过对象内置的属相__class__可以查看当前对象是属于哪个类型/或哪个类创建出来的
print(xiaoming.__class__) # <class '__main__.Person'>
__str__ 可打印协议方法,当对象被print打印时,自动执行
# 如果希望打印对象的时候,可以输出一些可控的信息,可以使用__str__或__repr__
class Person(object):
def __init__(self, name, sex):
self.name = name
self.sex = sex
def __str__(self):
# __str__必须有返回值,而且返回值的类型必须是str类型,否则报错
# return f"<{self.__class__.__name__}: {self.name}>"
print("Person.__str__")
return str(self.__dict__) # {'name': '小明', 'sex': 16}
def __repr__(self):
# __repr__ 必须有返回值,而且返回值的类型必须是str类型,否则报错
# __str__ 与 __repr__不能同时使用,否则只会输出__str__
print("Person.__repr__")
return "abc"

p = Person("小明", 16)
print(p) #Person.__str__ {'name': '小明', 'sex': 16}
# 原输出内容<__main__.Person object at 0x7f686295cfd0> 对象的引用地址信息

__repr__ 可打印协议方法,当对象被终端模式下不使用print而直接输出时,自动执行,类似__str__
__enter__ 执行上下文协议方法,当对象写在with语句后面时,自动执行
'''基于with上下文管理器自定义文件操作对象等操作'''
import pickle
class Pickle(object):
def __init__(self, path, mode="w"):
self.path = path
self.mode = mode
self.file = None

def __enter__(self):
self.file = open(self.path, self.mode)
return self

def write(self, message):
return pickle.dump(message, self.file)

def read(self):
return pickle.load(self.file)

def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()

with Pickle("1.db", "wb") as p:
p.write("hello")
with Pickle("1.db", "rb")as p:
print(p.read()) #hello
__exit__ 执行上下文协议方法,当对象写在with语句后面,而with语句执行结束时,自动执行
__iter__ 可迭代协议方法,当对象被当成可迭代对象调用,使用iter函数或者for循环时,自动执行
set_data = {"A", "B", "D", 100}
# data = iter(set_data) # 方法一. 可迭代对象转化迭代器
data = set_data.__iter__() # 方法二
print(data) #<set_iterator object at 0x107826040>
res = data.__next__()
print(res) #B
res = data.__next__()
print(res) #A
res = next(data) # next(data),就是 data.__next(),本质上没有区别,仅仅写法不同。
print(res) #100
res = next(data)
print(res) #D
res = next(data)
print(res) #StopIteration

 # 迭代器中所有值都被提取完成后,再次取值,python会以抛出一个StopIteration异常告诉我们,没有值了。这并不代表错误发生,而是一种迭代完成的标志,防止出现无限循环。

#迭代器的特性就是能够调用__next__方法依次计算出迭代器中的每一个值 ,然后每一个值重复的放在同一个内存空间


__next__ 迭代器协议方法,当对象被当成可迭代对象调用,for循环时,自动执行
__setitem__ 序列协议方法,当对象被当成序列操作,设置索引/键时,自动执行
'''序列协议方法,当对象被当成序列操作,设置、读取、删除索引/键时,自动执行。'''
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age

def __getitem__(self, item):
print('obj[key]取值时,执行__getitem__')
print("取值为:",self.__dict__[item])

def __setitem__(self, key, value):
print('obj[key]=value赋值时,执行__setitem__')
self.__dict__[key] = value

def __delitem__(self, key):
print('del obj[key]触发')
self.__dict__.pop(key)

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

# obj.["key"]的方式触发__xxxitem__魔法方法
xiaoming = Person("小明", 16)
name = xiaoming["name"] # 触发__getitem__执行
xiaoming["age"] = 18 # 触发__setattr__执行

del xiaoming["age"] # 触发__delitem__

print( len(xiaoming) ) #1
"""
字典/列表/元组/字符串等序列类型,之所以支持中括号取键/下标进行数据操作,就是因为这些数据都是对象,
而且实例化这些对象的类都统一的实现了序列协议方法
"""
data = {"name": "xiaohong"}
print(type(data), data.__class__) # <class 'dict'> <class 'dict'>
data = [1, 2, 3, 4]
print(type(data), data.__class__) # <class 'list'> <class 'list'>
__getitem__ 序列协议方法,当对象被当成序列调用,读取索引/键时,自动执行
__delitem__ 序列协议方法,当对象被当成序列调用,删除索引/键时,自动执行

 补充:

1 '''`__init__` 只能初始化当前类刚创建的对象,自动调用。
`__new__`可以返回其他类的对象,但不会触发当前类的`__init__`魔术方法。所以务必要返回当前类对象'''
class Animal(object):
    def __init__(self):
        print("Animal.__init__")

class Person(object):
    # __init__ 不是创建对象的魔术方法,而是刚创建(刚出生)的对象进行属性默认值的设置
    def __init__(self, name, age):
        print("Person.__init__")
        # __init__执行之前,类就已经完成了对象的创建
        print(self)  # <__main__.Person object at 0x7fa385167fd0>
        self.name = name
        self.age = age

    def __new__(cls, *args, **kwargs):
        print("Person.__new__")
        # __new__ 必须有返回值,而且返回值应该是当前类的实例对象.
        # return object.__new__(cls)
        # 否则不会触发当前类的__init__方法,而是触发了其他类的__init__方法
        return Animal()

p1 = Person("小明", 13) #Person.__new__  Animal.__init__
print(p1) #<__main__.Animal object at 0x10257d4f0>
print(type(p1)) #<class '__main__.Animal'>
2 '''单例模式'''
'''基于`__new__`上面的特点,我们可以控制类创建对象的过程。实现在整个程序运行过程中,保证一个类不管实例化多少次,生成的都是同一个对象。例如,购物车,日志,数据库操作等'''
class Singleton(object):
    # 定义一个私有的成员属性__instance , 用来存储接下来创建的唯一实例对象.
    __instance = None

    def __new__(cls, *args, **kwargs):
        # 如果类的私有成员属性__instance 不存在 是一个None, 那么通过父类object的new创建一个对象
        if cls.__instance is None:
            # 创建对象,把对象存在私有属性__instance当中
            # cls.__instance = super().__new__(cls)
            cls.__instance = object.__new__(cls) #创建并返回一个新对象

        # 将该类对象直接返回
        return cls.__instance

class Log(Singleton):
    def __init__(self, name):
        self.name = name

# 第一次 触发cls.__instance is None 结果为True, 所以创建对象
logger1 = Log("small_project")
print(logger1)  # <__main__.Log object at 0x7fbb670f9fa0>

# 第二次 触发__new__ 发现cls.__instance is None 结果为False 直接 return cls.__instance
logger2 = Log("small_project")
print(logger2)  # <__main__.Log object at 0x7fbb670f9fa0>

print(logger1 is logger2)  # True

 

标签:__,.__,进阶,self,多态,def,print,三大,name
来源: https://www.cnblogs.com/up-zm/p/16180843.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有