首页 > 其他分享 >面向对象OOP

面向对象OOP

时间:2023-01-05 17:13:03浏览次数:42  
标签:__ name self 面向对象 OOP print class def

面向对象OOP

面向对象

什么是面向对象: 
	是一种编程思想,核心是对象,程序就是一系列对象的集合,程序员负责调度控制这些对象来交互着完成任务: 
	案例: 西天取经
    	如来有一堆破书要传出去,他没有自己干,而是找了五个对象帮他去干
        如来只要负责控制调度的对象即可
        如果某个对象发生变化,也不会影响其他的对象,扩展性

类和对象

类和对象

# 定义类(类名见名知意,大驼峰命名法,如: Class_Name)
class Person: 
    pass
    
    
# 创建对象
p = Person()

属性

"""
属性可以写在类中: 是所有对象公共的 
也可以写在对象中: 是每个对象独有的
如果类中和对象中存在同一属性,先访问对象,再访问类 
"""


# 属性的增删改查

class Person: 
    """这是一个人的属性"""
    desc = "都会吃饭"
p = Person()

# 增加属性: 对象.属性 = 值
p.name = "李四"
print(p.desc, p.name)  # 都会吃饭 李四

# 删除属性: del 对象.属性
del p.name

# 修改属性: 对象.属性 = 新的值 
p.desc = "喝水"
print(p.desc)  # 喝水

# 查看属性: 对象.__dict__ 查看的是对象的所有属性而不包含类的
print(p.__dict__)  # {'desc':  '喝水'}

# 查看对象的类信息: 对象.__class__
print(p.__class__)  # <class '__main__.Person'>

# 查看对象的类名称: 对象.__class__.__name__
print(p.__class__.__name)  # Person

# 查看类的名称:类.__name__
print(Person.__name__)  # Person 

__init__方法

"""
__init__方法: 初始化方法,本质上就是一个函数 
	当实例化对象时,会自动执行__init__方法
	会自动将对象作为第一个参数传入,参数名称为self(不建议改)
	用于给对象赋初始值
	返回值只能是默认的None
"""


# __init初始化方法可以给对象赋初始化值和执行初始化操作
class Dog: 
    def __init__(self,kind,color): 
        print(self)  # <__main__.Dog object at 0x000001BA8B00BBE0>
        self.kind = kind
        self.color = color
        # 简便写法
        # los = locals()
        # los.pop("self")
        # self.__dict__.update(lcs)
        print("赋值以外的初始化操作")
d1 = Dog("二哈","黑白")
print(d1,d1.name,d1.age)  # <__main__.Dog object at 0x000001BA8B00BBE0> 二哈 黑白

绑定方法和非绑定方法

对象绑定方法

"""
默认情况下类中的方法都是对象绑定方法 
特殊之处: 
	当使用对象调用该函数时会自动传入对象本身,作为该函数第一个参数  
	当使用类来调用该函数时就是一个普通函数,有几个参数就得传几个参数
"""
 
    
class Student: 
    def __init__(self,name): 
        self.name = name
    def say_hi(self): 
        print("hello i am a student! my name: %s" % self.name)
stu1 = Student("jack",20,"male")
stu1.say_hi()  # hello i am a student! my name: jack
Student.say_hi(stu1)  # hello i am a student! my name: jack
print(type(stu1.say_hi))  # <class 'method'>
print(type(Student.say_hi))  # <class 'function'>

类绑定方法

"""
类绑定方法用 @classmethod 来装饰 
特殊之处: 不管用类还是对象调用,都会自动传入类本身,作为第一个参数

什么时候用对象绑定方法: 当函数逻辑需要访问对象中的数据时
什么时候用类绑定方法: 当函数逻辑需要访问类中的数据时
"""


class OldBoyStudent: 
    def __init__(self,name): 
        self.name = name
    @classmethod  # 类绑定方法,此时cls为类而不为对象
    def show_school(cls): 
        print(cls)

OldBoyStudent.show_school()  # <class '__main__.OldBoyStudent'>
print(OldBoyStudent)  # <class '__main__.OldBoyStudent'>

非绑定方法

# 或叫静态方法,即不需访问类的数据,也不需要访问对象的数据,不常用
# 语法:  @staticmethod 


"""
示例: 为学生类添加一个save方法,一个get方法 
	save: 将对象存储到文件中
	get: 从文件中获取对象
"""

import pickle

class Student: 
    def __init__(self,name): 
        self.name = name
    def save(self): 
        with open(self.name,"wb") as f: 
            pickle.dump(self,f)
    @staticmethod
    def get(name): 
        with open(name,"rb") as f: 
            obj = pickle.load(f)
            return obj

stu = Student("rose")
stu.save()

obj1 = stu.get("rose")
print(obj1.name)  # rose
obj2 = Student.get("rose")
print(obj2.name)  # rose

继承

继承语法

"""
a继承了b则a就能直接使用b的方法和属性
a称之为子类,b称之为父类或基类

在python中一个子类可以同时继承多个父类
"""

class Base: 
    desc = "这是一个基类"
    def show_info(self): 
        print(self.desc)
    def make_money(self): 
        print("一天赚一个亿...")

# 指定父类为Base
class SubClass(Base): 
    pass

obj = SubClass()
# 即使子类中什么都没有也可以使用父类中已有的内容
print(obj.desc)  # 这是一个基类
obj.make_money()  # 一天赚一个亿...

抽象

"""
将多个子类中相同的部分进行抽取,形成一个新的类,这个过程称之为抽象

正确使用继承: 先抽象在继承 
"""

# 抽取老师和学生中相同的部分形成person类
class Person: 
    def __init__(self, name, age, gender): 
        self.name = name
        self.age = age
        self.gender = gender
    def say_hi(self): 
        print("name: %s,age: %s,gender: %s" % (self.name,self.age,self.gender))

class Teacher(Person): 
    def teaching(self): 
        print("老师教学生写代码...")
        
class  Student(Person): 
    pass

t1 = Teacher("jack",20,"male")
t1.say_hi()
stu1 = Student("rose",18,"female")
stu1.say_hi()

属性的查找顺序

对象自己的 > 所在类中 > 父类 > 父类的父类 > ... > Object

派生和覆盖

"""
派生: 
	当子类中出现了与父类中不同的内容时,这个子类就称之为派生类 
	通常子类都会写一些新的代码,不可能与父类完全一样,既通常都是派生类,所以派生类指的就是子类
	
覆盖: 也称之为重写,当子类出现了与父类名称完全一致的属性或方法
"""

子类中访问父类的内容

"""
方式1:  super(当前类名称,self).要调的父类的属性或方法
方式2:  super().要调的父类的属性或方法  # py3的新语法,最常用的方式
方式3:  类名称.要调的父类的属性或方法(self)  # 方式3与继承无关,直接指定类名调用

当继承一个现有的类并且覆盖了父类的__init__方法时,必须在__init__方法的第一行调用父类的__init__方法,并传入父类所需的参数 
"""


class Parent: 
    text = "abc"
    def say_something(self): 
        print("anything")

class Sub(Parent): 
    def show_info(self): 
        # 方式一: 
        # print(super(Sub,self).text)
        # super(Sub,self).say_something()
        # 方式二: 
        # print(super().text)
        # super().say_something()
        # 方式三: 
        print(Parent.text)
        Parent.say_something(self)

s = Sub()  # abc
s.show_info()  # anything




class Person: 
    def __init__(self,name,gender,age): 
        self.name = name
        self.gender = gender
        self.age = age
    def say_hi(self): 
        print("name: %s, gender: %s, age: %s" % (self.name,self.gender,self.age))

class Student(Person): 
    def __init__(self,name,gender,age,number): 
        super().__init__(name,gender,age)
        self.number = number
    def say_hi(self): 
        super().say_hi()
        print("numnber: %s" % self.number)

stu = Student("rose","mael",20,"boy01")
stu.say_hi()




# 实现一个能够限制元素类型的列表类

class MyList(list):
    def __init__(self,element_type):
        super().__init__()  # 调用父类的初始化方法来完成基本初始化
        self.element_type = element_type

    def append(self, object):
        """
        :param object: 要存储的元素
        :return: 没有
        """
        if type(object) == self.element_type:  # 当列表要添加的元素的类型和指定的类型相同时
            # 这里访问父类的append函数来完成真正的存储操作
            super(MyList,self).append(object)
            # super().append(object)
            # MyList.append(self,object)  # RecursionError: maximum recursion depth exceeded while calling a Python object
        else:
            print("sorry, your element type not is %s" % self.element_type)

m = MyList(int)  # 创建对象时指定要存储的元素类型
m.append(1)
print(m[0])  # 1
m.append("111")  # sorry, your element type not is int

组合

"""
将一个对象作为另一个对象的属性(既什么有什么)
什么时候用组合: 如果两个类之间完全不属于同类,但需要满足什么有什么  
"""


class Phone: 
    def __init__(self, kind, color): 
        self.kind = kind
        self.color = color
    def call(self): 
        print("正在呼叫XXX...")
        
class Student: 
    def __init__(self, name, phone): 
        self.name = name
        self.phone = phone

phone1 = Phone("apple", "red")
stu1 = Student("rose", phone1)
stu1.phone1.call()

菱形继承

"""
python支持多继承
python3中所有类都直接或间接继承了Object 

新式类与经典类: 
	新式类: 任何显式或隐式的继承自object类就称之为新式类,python3中都是新式类  
	经典类: 即不是Object的子类,仅在python2中出现
	
菱形继承时: 
	新式类: 先深度,当遇到了共同父类时就广度
	金典类: 就是深度优先
"""

  
class A: 
    pass

class B: 
    pass

class Test(A,B): 
    pass

print(Test.mro())  # [<class '__main__.Test'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]



# 菱形继承
class B: 
    num = 2
    pass

class C: 
    num = 3
    pass

class E(B): 
    num = 5
    pass

class F(C): 
    num = 6
    pass

class G(C): 
    num = 7
    pass

class H(E,F,G): 
    # num = 8
    pass

print(H.num)
print(H.mro())  # [<class '__main__.H'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.F'>, <class '__main__.G'>, <class '__main__.C'>, <class 'object'>]

封装

封装语法

"""
什么是封装: 对外隐藏内部实现细节,并提供访问的接口

什么时候应该封装: 当有一些数据不希望外界可以直接修改时,当有一些函数不希望给外界使用时

被封装内容的特点: 外界不能直接访问,内部依然可以使用

权限: 封装可以控制属性的权限,在python只有两种权限
	公开的: 默认就是公开的
	私有的: 只能由当前类自己使用
	
语法: 将要封装的属性或方法名称前加上双下划线

python实现封装的原理: 
在加载类的时候,把__替换成了_类名__,替换变量名称 
"""


# 封装属性
class Person: 
    def __init__(self, id_number, name, age): 
        self.__id_number = id_number  # 双下划线封装后则不能修改内部的值
        self.name = name
        self.age = age
    def show_id(self): 
        print(self.__id_number)

p = Person("123456789", "jack", 29)
# print(p.__id_number)  # AttributeError:  'Person' object has no attribute '__id_number'
p.__id_number = "123"
print(p.__id_number)  # 123
p.show_id()  # 123456789



# 封装方法
class PC: 
    def __init__(self,kind,color): 
        self.kind = kind
        self.color = color
    def open(self): 
        print("接通电源")
        self.__check_device()
        print("初始化内核")
        self.__start_services()
        print("启动GUI")
    def __check_device(self): 
        print("硬件检测")
    def __start_services(self): 
        print("启动服务")

pc1 = PC("苹果","红色")
pc1.open()
pc1.__check_device()  # AttributeError:  'PC' object has no attribute '__check_device'



# python实现封装的原理
class A: 
    def __init__(self,key): 
        self.__key = key
    def key(self): 
        return self.__key

a = A("123")
print(a.__key)  # AttributeError:  'A' object has no attribute '__key'
print(a.__dict__)  # {'_A__key':  '123'}
print(a._A__key)  # 123
a.__key = 1
print(a.__dict__)  # {'_A__key':  '123', '__key':  1}

外界访问私有内容

# 通过定义方法来完成对私有属性的访问和修改,则可以在外界修改这个关键数据时做一些限制 


class Downloader: 
    def __init__(self,url,buffer_size): 
        self.url = url
        self.__buffer_size = buffer_size
    def start_download(self): 
        if self.__buffer_size <= 1024*1024: 
            print("开始下载...")
            print("当前缓冲器大小: ",self.__buffer_size)
        else: 
            print("内存炸了...")
    def set_buffer_size(self,size): 
        # 定义方法来完成对私有属性的访问和修改
        if not type(size) == int: 
            print("缓冲区大小必须是整型...")
        else: 
            self.__buffer_size = size
            print("缓冲区大小修改成功...")
    def get_buffer_size(self): 
        return self.__buffer_size

d = Downloader("http: //www.baicu.com",1024*1024)
# 通过函数去修改内部封装的属性
d.set_buffer_size(1024*2048)
# 通过函数访问内部封装的属性
print(d.get_buffer_size())
d.start_download()

property装饰器

"""
通过定义方法来完成对私有属性的访问和修改,给对象的使用者带来了麻烦,必须知道哪些是普通属性哪些是私有属性,需要使用不同的方式来调用他们,property装饰器就是为了使得调用方式一致  
使用property装饰器可以将私有属性伪装成普通属性,使得私有属性调用方法跟普通属性一致

property: 用在获取属性的方法上 
@属性名.setter: 用在修改属性的方法上 
@属性名.deleter: 用在删除属性的方法上 
注意: 内部会创建一个对象,对象名称为属性名称,所以在使用setter和deleter时,必须使用对象名称去调用方法 

property装饰器还可以用来实现计算属性
计算属性: 属性的值不能直接获得,必须通过计算才能获取,例如: 正方形求面积 
"""

   
class A: 
    def __init__(self,name,key): 
        self.name = name
        self.__key = key
    @property
    def key(self): 
        return self.__key
    @key.setter
    def key(self,new_key): 
        if new_key <= 100: 
            self.__key = new_key
        else: 
            print("key必须小于等于100")
    @key.deleter
    def key(self): 
        del self.__key

a = A("jack",123)
print(a.key)  # 123
a.key = 99
print(a.key)  # 99
del a.key  
print(a.key)  # AttributeError:  'A' object has no attribute '_A__key'




# 问题
class Square: 
    def __init__(self,width): 
        self.width = width
        self.area = self.width * self.width

s = Square(10)
print(s.area)  # 100
s.width = 2
print(s.area)  # 100

# property装饰器实现计算属性
class Square: 
    def __init__(self,width): 
        self.width = width
    @property
    def area(self): 
        return self.width * self.width

s = Square(10)
print(s.area)  # 100
s.width = 2
print(s.area)  # 4

接口

"""
接口是一组功能的集合,接口中仅包含功能的名字,不包含具体的实现代码 
接口本质是一套协议标准,遵循这个标准的对象就能被调用
接口方便了对象的使用者,只要学习一套使用方法,就可以以不变应万变 
问题: 如果子类没有按照协议来设计,也没办法限制他,将导致代码无法运行
"""


# 案例: 电脑提前制定一套USB接口协议,PC的代码一旦完成,后期无论什么样的设备,只要遵循了USB接口协议,都能够被电脑所调用

class USB: 
    def open(self): 
        pass
    def read(self): 
        pass
    def write(self): 
        pass
    def close(self): 
        pass
    
def pc(usb_device): 
    usb_device.open()
    usb_device.read()
    usb_device.write()
    usb_device.close()

class Mouse(USB): 
    def open(self): 
        print("鼠标开机...")
    def read(self): 
        print("获取光标位置...")
    def write(self): 
        print("鼠标不支持写入...")
    def close(self): 
        print("鼠标关机...")

class KeyBoard(USB): 
    def open(self): 
        print("键盘开机...")
    def read(self): 
        print("获取按键字符...")
    def write(self): 
        print("键盘敲入代码...")
    def close(self): 
        print("键盘关机...")

# 鼠标对象
m = Mouse()
# 将鼠标传给电脑
pc(m)

# 键盘对象
k = KeyBoard()
# 将键盘传给电脑
pc(k)

鸭子类型

"""
python一般不会限制你必须怎么写,但作为一个优秀的程序员,就应该自觉遵守相关协议,所以有了鸭子类型这么一说
只要保证你的类按照相关协议类编写,也可以达到提高扩展性的目的  
接口是一套协议规范,明确子类应该具备哪些功能
抽象类是用于强制要求子类必须按照协议中规定的来实现  
然而python不推崇限制你的语法,可以设计成鸭子类型,既让多个不同类对象具备相同的属性和方法,对于使用者而言,轻松的使用各种对象 
"""
    
    
def pc(usb_device): 
    usb_device.open()
    usb_device.read()
    usb_device.write()
    usb_device.close()

class Mouse(): 
    def open(self): 
        print("鼠标开机...")
    def read(self): 
        print("获取光标位置...")
    def write(self): 
        print("鼠标不支持写入...")
    def close(self): 
        print("鼠标关机...")

class KeyBoard(): 
    def open(self): 
        print("键盘开机...")
    def read(self): 
        print("获取按键字符...")
    def write(self): 
        print("键盘敲入代码...")
    def close(self): 
        print("键盘关机...")

# 鼠标对象
m = Mouse()
# 将鼠标传给电脑
pc(m)

# 键盘对象
k = KeyBoard()
# 将键盘传给电脑
pc(k)

抽象类

"""
抽象类(abstract class): 包含抽象方法(没有函数体的方法)的类,
作用: 可以限制子类必须有父类中定义的抽象方法  
"""

import abc

# 子类必须要定义run方法,否则会报错
class AClass(metaclass=abc.ABCMeta): 
    @abc.abstractmethod
    def run(self): 
        pass

class B(AClass): 
    def run(self): 
        print("run...")
        
b = B()

多态

"""
概念: 多个不同类对象可以响应同一个方法,产生不同的结果,既多个不同类对象有相同的使用方法,
实现多态: 接口、抽象类、鸭子类型都可以写出具备多态的代码
"""


# 案例: 
class Ji: 
    def bark(self): 
        print("咯咯咯")
    def spawn(self): 
        print("下鸡蛋...")

class Ya: 
    def bark(self): 
        print("嘎嘎嘎")
    def spawn(self): 
        print("下鸭蛋...")

class E: 
    def bark(self): 
        print("饿饿饿")
    def spawn(self): 
        print("下鹅蛋...")

j = Ji()
y = Ya()
e = E()

def manage(obj): 
    obj.spawn()

manage(j)
manage(y)
manage(e)



# python中到处都有多态
a = 10
b = "10"
c = [10]
print(type(a))
print(type(b))
print(type(c))

OOP相关内置函数

#1. isinstance(对象,类型) 判断对象是否是某个类的实例

def add_num(a,b): 
    if isinstance(a,int) and isinstance(b,int): 
        return a+b
    return None
print(add_num(20,10))


#2. issubclass(type(子类),父类) 判断一个类是否是另一个类的子类 

参数一是子类

参数二是父类  

class Animal: 
    def eat(self): 
        print("动物得吃东西...")

class Pig(Animal): 
    def eat(self): 
        print("猪得吃猪食...")

class Tree: 
    def light(self): 
        print("植物光合作用...")

def manage(obj): 
    if issubclass(type(obj),Animal): 
        obj.eat()
    else: 
        print("不是动物!")

pig = Pig()
tree = Tree()
manage(pig)
manage(tree)

类中的魔法函数

__str__

"""
执行时机: 在对象被转换为字符串时执行,转换的结果就是__str__函数的返回值 
使用场景: 自定义对象的打印格式(print打印会转成字符串,所以会触发__str__)
"""

class Person: 
    def __str__(self): 
        print("run")
        return "person"

p = Person()
print(p)  # run  person 
str(p)  # run
print(str(p))  # run  person

__del__

"""
执行时机: 手动删除对象时立即执行,或是程序运行结束时也会自动执行 
使用场景: 当对象在使用过程中,打开了不属于解释器的资源,例如: 文件,网络端口 
"""

class Person:
    def __del__(self):
        print("del run")

p = Person()
del p  # del run



# 案例: 

class FileTool:
    """该类用于简化文件的读写操作 """
    def __init__(self,path):
        self.file = open(path,"rt",encoding="utf-8")
    def read(self):
        return self.file.read()
    def __del__(self):
        self.file.close()

tool = FileTool("a.txt")
print(tool.read())

__call__

# 执行时机: 在调用对象时自动执行(既对象加括号)

class A: 
    def __call__(self, *args, **kwargs): 
        print("call run")
        print(args)
        print(kwargs)

a = A()
a(1,a=100)  # call run  (1,)  {'a': 100}

__slots__

"""
是一个类属性,用于优化对象内存占用
优化的原理: 固定属性数量,则这个类的对象无法再添加新的属性,解释器不会为这个对象创建名称空间,则没有__dict__
"""

import sys

class Person: 
    __slots__ = ["name"]
    def __init__(self,name): 
        self.name = name

p = Person("jack")
print(sys.getsizeof(p))  # 对象占用的内存空间
p.age = 20  # AttributeError:  'Person' object has no attribute 'age'
print(p.__dict__)  # AttributeError:  'Person' object has no attribute '__dict__'

. 语法实现原理

"""
getattr 用 对象.属性 访问属性而属性不存在时执行
setattr 用 对象.属性=值 设置属性时执行
delattr 用 del 对象.属性 删除属性时执行
getattribute 也是用来获取属性,如果属性存在则先执行getattribute再直接返回值,如果属性不存在则先执行getattribute再执行getattr再返回None
"""


class A:
    def __getattr__(self,item):
        print("__getattr__")
    def __setattr__(self,key,value):
        # super().__setattr__(key,value)  # 等价于 self.__dict__[key] = value
        print(key)
        print(value)
        self.__dict__[key] = value
        print("__setattr__")
    def __delattr__(self,item):
        self.__dict__.pop(item)
        print("__delattr__")
    def __getattribute__(self,item):
        print("__getattribute__")
        # return self.__dict__[item]  # RecursionError: maximum recursion depth exceeded while calling a Python object
        return super().__getattribute__(item)

# 没有定义__getattribute__时
a = A()
a.name = 'jll'  # __setattr__
print(a.name)  # jll
a.__dict__['age'] = 18
print(a.age)  # 18
del a.name  # __delattr__
print(a.name)  # __getattr__

# __getattribute__测试
b = A()
b.name = 'xxx'
print(b.name)  # 当name存在时执行__getattribute__再返回值,当name不存在存在时先执行__getattribute__再执行__getattr__再返回None

[] 语法实现原理

"""
任何符号都会被解释器解释成特殊含义,例如: . [] () 

getitem 用中括号获取属性时执行
setitem 用中括号设置属性时执行
delitem 用中括号删除属性时执行
"""


class A:
    def __getitem__(self,item):
        print("__getitem__")
        return self.__dict__[item]
    def __setitem__(self,key,value):
        print("__setitem__")
        self.__dict__[key] = value
    def __delitem__(self,key):
        print("__delitem__")
        del self.__dict__[key]

a = A()
a["name"] = "jack"  # __setitem__
print(a["name"])  # __getitem__
del a["name"]  # __delitem__



# 示例: 让对象既支持.取值又支持[]取值

class MyDict(dict):
    def __getattr__(self,key):
        return self.get(key)  # 或者 return self[key]
    def __setattr__(self,key,value):
        self[key] = value
    def __delattr__(self,key):
        del self[key]

a = MyDict()

a["name"] = "jack"
print(a["name"])
print(a.__dict__)  # {}
del a["name"]

a.age = 18
print(a.age)
print(a.__dict__)  # {}
del a.age

运算符重载

# 当使用某个符号时,python解释器会为这个符号定义一个含义,同时调用对应的处理函数,当需要自定义对象的比较规则时,就可以在子类中覆盖大于、小于、等于等一系列方法

# 案例: 原本自定义对象无法使用大于小于等于来进行比较,可以自定义运算符来实现,让自定义对象也支持比较运算符  

class Student():
    def __init__(self,name,height):
        self.name = name
        self.height = height
    def __gt__(self, other): # other指的是另一个参与比较的对象
        print("__gt__")
        return self.height > other.height
    def __lt__(self, other):  # 大于和小于只要实现一个即可,符号如果不同,解释器会自动交换两个对象的位置 
        print("__lt__")
        return self.height < other.height
    def __eq__(self, other):
        print("__eq__")
        if self.name == other.name and self.age == other.age:
            return True

stu1 = Student("jack",180)
stu2 = Student("rose",160)
stu3 = Student("jack",180)
print(stu1 > stu2)  # __gt__  True
print(stu1 < stu2)  # __lt__  False
print(stu1 == stu3)  # __eq__  True

迭代器方法

# 迭代器是指具有__iter__和__next__的对象,可以为对象增加这两个方法来让对象变成一个迭代器 


# 实现自定义range

class MyRange:
    def __init__(self,start,end,step):
        self.start = start
        self.end = end
        self.step = step
    def __iter__(self):
        return self
    def __next__(self):
        a = self.start
        self.start += self.step
        if a < self.end:
            return a
        else:
            raise StopIteration

for i in MyRange(1,10,2):
    print(i)  # 1  3  5  7  9

上下文管理

"""
在python中,上下文为是一段代码区间,例如: with open打开的文件仅在这个上下文中有效 

上下文管理两个方法:
	__enter__: 表示进入上下文,应该返回对象自己
	__exit__: 表示退出上下文,可以有bool类型返回值,用于表示异常是否被处理,仅在上下文中出现异常有用
		如果为True,表示异常已经被处理
		如果为False,表示异常未被处理,程序将中断

当执行with语句时,会先执行__enter__,当代码执行完后再执行__exit__,或者代码异常会立即执行__exit__,并传入错误信息(exc_type(包含错误的类型).exc_val(错误的信息).exc_tb(错误的追踪信息))
""" 


class MyOpen(object):
    def __init__(self,path):
        self.path = path
    def __enter__(self):
        self.file = open(self.path)
        print("enter...")
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("exit...")
        self.file.close()
        # return False

with MyOpen("a.txt") as m:
    # "123"+1
    print(m.file.read())

反射reflect

反射

"""
反射: 一个对象应该具备检测,增加,修改,删除自身属性的能力,就是通过方法操作对象属性(直接使用__dict__语法繁琐,其实这些方法就是对__dict__的操作进行了封装)
反射的四个内置函数: 
	hasattr(对象,"属性")  判断对象是否存在某个属性
	getattr(对象,"属性")  获取对象属性,可以设置默认值: getattr(对象,"属性",默认值)  
	setattr(对象,"属性",值)  修改或添加对象属性
	delattr(对象,"属性")  删除对象属性
"""


class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age
        
p = Person("jack",18)
print(hasattr(p,"name"))  # True
print(getattr(p,"name"))  # jack
print(getattr(p,"names",None))  # None
setattr(p,"name","jll")
print(p.name)  # jll
setattr(p,"id","123")
print(p.id)  # 123
delattr(p,"id")

反射使用案例:框架

框架代码

"""
框架设计方式:
	当对象不是自己写的而是另一方提供的,就必须判断这个对象是否满足要求,是否是我需要的属性和方法
	反射被称为框架的基石,因为框架的设计者,不能提前知道你的对象是怎么设计的,所以提供给框架的对象,必须通过检测之后才能使用,检测就是反射要做的事情
	
示例: 用于处理用户终端指令的小框架	
	如果框架代码中,写死了使用某个类,这是不合理的,因为无法提前知道框架使用者的类在什么地方类叫什么
	所以应该为框架的使用者提供一个配置文件,要求对方将类的信息写入配置文件,然后框架自己去加载需要的模块
	如此一来,框架就与实现代码彻底解耦了,只剩下配置文件
"""


import importlib
import settings

# 框架已经实现的部分
def run(plugin):
    while True:
        cmd = input("请输入指令:")
        if cmd == "exit":
            break
        # 因为无法确定框架使用者是否传入正确的对象所以需要使用反射来检测
        # 判断对象是否具备处理这个指令的方法
        if hasattr(plugin,cmd):
            # 取出对应方法
            func = getattr(plugin,cmd)
            func()  # 执行方法处理指令
        else:
            print("该指令不受支持...")
    print("see you la la!")
    
# 框架根据配置文件拿到需要的类的路径
path = settings.CLASS_PATH
# 从配置中单独拿出模块路径和类名称
module_path,class_name = path.rsplit(".",1)
# 根据模块的路径拿到模块
mk = importlib.import_module(module_path)
# 从模块中取出类
cls = getattr(mk,class_name)
# 实例化对象
obj = cls()
# 调用框架
run(obj)

插件部分(libs\plugins.py)

class WinCMD:
    def cd(self):
        print("wincmd 切换目录...")
    def delete(self):
        print("wincmd 删库跑路...")
    def dir(self):
        print("wincmd 列出所有文件...")

class LinuxCMD:
    def cd(self):
        print("Linuxcmd 切换目录...")
    def rm(self):
        print("Linuxcmd 删库跑路...")
    def ls(self):
        print("Linuxcmd 列出所有文件...")

配置文件

# 框架使用者在配置文件中指定配合框架的类是哪个
CLASS_PATH = "libs.plugins.LinuxCMD"

动态导入importlib模块

import importlib

# 根据模块的路径拿到模块
pl = importlib.import_module("libs.plugins")  # 等价于 import libs.plugins

# 从模块中取出类
cls = getattr(pl,"WinCMD")

# 实例化产生对象
obj = cls()
obj.cd()

元类metaclass

元类

"""
元类: 用于创建类的类,万物皆对象,类也是对象,对象是通过类实例化产生的,类对象也是由另一个类实例化产生的,这个类就是元类,默认情况下所有类的元类都是type 
"""


class Person(object):
    pass
p = Person()
print(type(p))  # <class '__main__.Person'>
print(type(Person))  # <class 'type'> Person类是通过type类实例化产生的


# 直接调用type类来产生类对象
# 类的三个基本组成: 1.类的名字(字符串)  2.类的父类们(列表或元组)  3.类的名称空间(字典)
cls_obj = type("dog",(),{})
print(cls_obj)  # <class '__main__.dog'>

自定义元类

"""
元类的作用:创建类对象时做一些限制时可以操作元类,即继承type类来编写自己的元类(只要继承了type就变成了元类),并覆盖__init__方法
"""


# 自定义一个元类
class MyType(type):
    def __init__(self, clss_name, bases, namespace):
        super().__init__(clss_name, bases, namespace)
        print(clss_name, bases, namespace)
        if not clss_name.istitle():
            raise Exception("类名首字母需大写...")

# 为pig类指定元类为MyType,等价于 MyType("Pig",(),{})
class Pig(metaclass=MyType):  # Pig () {'__module__': '__main__', '__qualname__': 'Pig'}
    pass

class Pig(metaclass=MyType):  # Exception: 类名首字母需大写...
    pass

元类的__call__方法

"""
当调用类对象(类加括号)时会自动调用元类中的__call__方法,并将这个类作为第一个参数传入
类的元类覆盖byte类的__call__方法后,这个类就无法产生对象,元类必须调用super().__call__来完成对象的创建,并将其对象返回
作用:控制对象的创建过程,可以元类覆盖byte类的__call__方法
"""


# 示例1:把对象的所有属性变成大写

class MyMeta(type):
    def __init__(self,name,bases,namespace):
        super().__init__(name,bases,namespace)
        print("init run")
    def __call__(self, *args, **kwargs):
        print("metaclass call run")
        print(self)  
        print(args)
        print(kwargs)
        new_args = []
        for a in args:
            new_args.append(a.upper())
        return super().__call__(*new_args,**kwargs)

class Dog(metaclass=MyMeta):  # <==> MyMate("Dog",(),{}),此时会执行元类的__init__方法
    def __init__(self,name,hobby):
        self.name = name
        self.hobby = hobby
    def __call__(self, *args, **kwargs):  # 不会执行类中的__call__方法,会执行元类中的__call__方法
        print(self)
        print("call run")

d = Dog("jack","study")  # init run \ metaclass call run \ <class '__main__.Dog'> \ ('jack', 'study') \ {}
print(d)  # <__main__.Dog object at 0x00000273BC857EE0>
print(d.name,d.hobby)  # JACK STUDY



# 示例2:要求创建对象时必须以关键字参数形式来传参, (不能有位置参数,有就 抛异常)

class Mate(type):
    def __call__(self, *args, **kwargs):
        if args:
            raise Exception("不允许使用位置参数!")
        return super().__call__(*args,**kwargs)
    # def __call__(self, *args, **kwargs):
    #     # 元类__call__方法所干的事如下:
    #     obj = object.__new__(self)  # 创建一个空类对象
    #     self.__init__(obj,*args,**kwargs)  # 让类对象去初始化
    #     return obj  # 返回类对象

class A(metaclass=Mate):
    def __init__(self,name):
        self.name = name

a = A(name="jack")

元类的__new__方法

"""
当创建类对象时,会先执行元类中的__new__方法,拿到一个空对象,再调用__init__来对这个类进行初始化操作(__new__用于创建类对象并且自动调用__init__让对象去初始化)  
注意:自定义元类覆盖了__new__方法,则__new__方法必须返回对应的类对象
总结: __new__方法和__init__都可以实现控制类的创建过程,__init__更简单
"""


class Meta(type):
    def __new__(cls, *args, **kwargs):
        print(cls)  # <class '__main__.Meta'> 元类自己
        print(args)  # ('A', (), {'__module__': '__main__', '__qualname__': 'A'}) 创建类需要的参数: 类名,基类,名称空间
        print(kwargs)  # {} 空的
        print("new run")  # new run
        return super().__new__(cls, *args, **kwargs)
    def __init__(self,a,b,c):
        super().__init__(a,b,c)
        print("init run")  # init run

class A(metaclass=Meta):
    pass

单例设计模式

"""
单例:指的是一个类产生一个对象
为什么要使用单例:单例是为了节省资源,当一个类的所有对象的属性全部相同时,则没有必要创建多个对象
"""


# 方法1:
# 单例元类
class Single(type):
    def __call__(self, *args, **kwargs):
        if hasattr(self,"obj"):  # 判断是否已存在对象
            return getattr(self,"obj")  # 有就返回
        obj = super().__call__(*args,**kwargs)  # 没有则创建
        self.obj = obj  # 并存入类中
        print("new 了")
        return obj

class Student(metaclass=Single):
    def __init__(self,name):
        self.name = name

# 只会创建一个对象
Student("jll1")
Student("jll2")



# 方法2:
class Mysql:
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = object.__new__(cls, *args, **kwargs)
        return cls._instance
    
a = Mysql()
b = Mysql()
print(a)  # <__main__.Mysql object at 0x0000022511613580>
print(b)  # <__main__.Mysql object at 0x0000022511613580>

标签:__,name,self,面向对象,OOP,print,class,def
From: https://www.cnblogs.com/jlllog/p/17028152.html

相关文章

  • 描述大数据的技术生态?Hadoop、Hive、Spark 之间是什么关系
    作者:小怪兽链接:https://www.zhihu.com/question/27974418/answer/1862026844来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。1Hadoop只是......
  • Hadoop中的MapReduce框架
    一个MapReduce作业通常会把输入的数据集切分为若干独立的数据块,由Map任务以完全并行的方式去处理它们。          框架会对Map的输出......
  • hadoop fs:du统计hdfs文件(目录下文件)大小的用法
     [root@node218~]#hadoopfs-du-h/inventory5g8.0G8.0G/inventory5g/inventory.dat261.2M261.2M/inventory5g/inventory_1_30.dat261.6M261......
  • hadoop中MapReduce配置
    一,配置mapred-site.xml进入以入目录[root@hadoop01hadoop]#cd/home/software/hadoop-2.7.1/etc/hadoop复制mapred-site.xml示例文件[root@hadoop01hadoop]#cpmapred-s......
  • [欢迎报名]肖鹏-UML在动态面向对象设计中的应用-UMLChina讲座
    时间北京时间2020年10月16日(周五)19:00-20:00地点通过腾讯会议举行,会议号:6817742045。费用不收费专家肖鹏,ZenUML.com项目创始人,动态面向对象建模技术推动者。肖鹏曾任Tho......
  • Hadoop知识简要
      1、HDFSHDFS是分布式文件系统-支持海量数据存储-由大量的计算机集群组成-可以运行再低成本的硬件之上-高容错性-高可靠性-高扩展性-高吞吐率适合大......
  • 【C++高级编程】(三)面向对象设计
    本章内容:什么是面向对象的程序设计如何定义不同对象之间的关系抽象的重要性以及如何在设计中使用抽象()  3.1过程化的思考方式    3.2面向对象思想......
  • 面向对象
    面向对象oop面向对象的本质就是,以类的方式组织代码,以对象的组织(封装)数据。抽象三大特征:封装,继承,多态值传递与引用传递packageoop;//值传递publicclassDemo1{......
  • ABAP开发面向对象---类
    今日学习ABAP面向对象里面的类,关于构造,继承,实现。踩坑点:类有抽象的方法,类本身也需要是抽象的,故需要在类申明里面加上ABSTRACT关键字学习资料为B站翱翔云天老师的1CLA......
  • php面向对象(OOP)编程
    大多数类都有一种称为构造函数的特殊方法。当创建一个对象时,它将自动调用构造函数,也就是使用new这个关键字来实例化对象的时候自动调用构造方法。构造函数的声明与其它操......