python基础---面向对象相关知识

news2025/1/9 17:04:35

面向对象

可以把数据以及功能打包为一个整体

类:

  • 名称
  • 属性(数据)
  • 方法
class Person:
    def __init__(self, name, age):
        self.age = age
        self.name = name
    def print_info:
        print(self.name, self.age)

定义

#经典类
class Dog1:
    pass

# 新式类
class Dog2(object):
    pass

在python3里面这都一样, 用于继承

self

self指向当前的这一个对象自己, python会自动传递当前的对象

self的名字可以不叫self, 但是推荐使用self, 会使用类的方法的第一个参数

属性

class Dog1:
    def set_name(self, name):
        self.name = name
    def print_name(self):
        print(self.name)

class Dog2:
    pass

dog1 = Dog1()
# dog1.set_name('dog1')
dog1.print_name()

这一个属性会在调用dog1.set_name('dog1')的时候才会创建, 不调用的时候使用这一个变量会出问题

也可以使用dog1.name = "dog1"来进行指定, 但是一般不使用这一个方式

私有属性

普通的属性可以在外部进行修改, 这种方式一般是不希望出现的

可以在变量名前面加两个__进行设置

class Dog1:
    def __init__(self, name):
        self.__name = name
    def print_name(self):
        print(self.__name)
 
dog = Dog1("dog1")
print(dog.__name)
PS E:\JHY\python\2024-4-22> python -u "e:\JHY\python\2024-4-22\main.py"
Traceback (most recent call last):
File "e:\JHY\python\2024-4-22\main.py", line 9, in <module>
 print(dog.__name)
AttributeError: 'Dog1' object has no attribute '__name'
dog = Dog1("dog1")
dog.__name = "dog2"
dog.print_name()

这样实际也不能修改这一个变量, 打印的还是dog1

实际使用的时候可以通过_ClassName__name进行调用, 但是不建议使用

dog = Dog1("dog1")
dog._Dog1__name = "dog2"
dog.print_name()

python实际是对这一个变量进行了一个重命名

私有方法

外部不能直接调用的方法

在函数名的前面加一个__

继承

一个新的类里面有之前的一个类里面的数据, 这时候可以使用继承的方式

class NewClass(OldClass1, OldClass2, ...):
    pass

实际继承的时候可以有多个类

继承获取的方法可以重写进行覆盖(方法的名字需要一样)

class Parent(object):
    x = 1

class son1(Parent):
    pass

class son2(Parent):
    pass

print(Parent.x, son1.x, son2.x)
son1.x = 2			# 这一个实际是一个定义
print(Parent.x, son1.x, son2.x)
Parent.x = 3
print(Parent.x, son1.x, son2.x)
PS E:\JHY\python\2024-4-22> python -u "e:\JHY\python\2024-4-22\main.py"
1 1 1
1 2 1
3 2 3

在类里面的变量是使用字典的方式进行记录的, 在当前的类里面没有找到的话会从祖先的类里面找, 子类里面重写会创建一个新的变量在他的里面记录这一个新的值, 不会影响父类里面的值

super

有可能存在父类的方法不可以满足子类的需求, 但是不是需要全部重写, 而是加了一部分新的数据, 全部重写会出现代码的冗余

可以重写的时候调用一下父类的方法

class Chile(Father):
    def Father_func():
        super.Father_func()
        子类的新的处理

也可以使用Father.Father_func(), 但是使用这一个方法的时候会导致多继承的时候可能有的函数会被多次调用, 如果没有相同的祖先, 这一个就不会出现问题

class Father(object):
    def __init__(self, name):
        print("Father")
        self.name = name

class Son1(Father):
    def __init__(self, name):
        print("Son1 Begin")
        super().__init__(name)
        print("Son1 End")

class Son2(Father):
    def __init__(self, name):
        print("Son2 Begin")
        super().__init__(name)
        print("Son2 End")

class GrandSon(Son1, Son2):
    def __init__(self, name):
        print("GrandSon")
        super().__init__(name)
        print("GrandSon End")

grandson = GrandSon("Shi")
PS E:\JHY\python\2024-4-22> python -u "e:\JHY\python\2024-4-22\main.py"
GrandSon
Son1 Begin  
Son2 Begin  
Father      
Son2 End    
Son1 End    
GrandSon End

这一个调用的时候需要计算出来实际调用的是哪一个父类, python里面实际使用了一个C3算法

可以使用print(GrandSon.__mro__)查看实际调用的顺序, 每一次遇到一个super会进入下一层的函数里

(<class '__main__.GrandSon'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Father'>, <class 'object'>)

使用这一个的时候会出现一个问题: 多进程的时候传入的参数的个数的问题, 这一个的顺序是不一定的, 所以传递的时候需要把所有的参数都传递

多态

在python里面不是很明显

多态是指一类事物有多种形态,比如动物类,可以有猫,狗,猪等等。(一个抽象类有多个子类,因而多态的概念依赖于继承)

多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。

实际使用的时候可以传递一系列类过去, 这些类里面有一个相同名字的函数, 但是这一个函数的实际执行的任务不同, 传不同的类会使用不同的行为, 但是实际调用的时候看起来调用的都一样

特殊属性方法

静态方法

在class里面有一个函数, 实际不需要class这一个参数, 这一个方法就是一个静态方法, 实际使用的时候需要使用参数@staticmethod进行修饰

class Class(Object):
    @staticmethod
    def func():
        pass

这一个静态方法可以直接使用类名进行调用

Class.func()

类属性

通过一个类创建的对象之间的数据是相互隔离的, 但是有的数据需要是共享的

在Class里面, 但是位于def外面的属性是一个类属性

class Class(object):
    类属性 = value

实际调用的时候需要使用类名.类属性来调用这一个属性

class Tools(object):
    num = 0
    def __init__(self):
        Tools.num += 1

类里面的属性实际存储的时候使用的是字典里面的键值对, 所以实际去调用一个参数的时候传给属性拦截器的是一个字符串

类方法

专门用于对类属性操作的方法, 使用@classmethod, 这个时候传递的参数是cls

class Tools(object):
    num = 0
    @classmethod
    def add_1(cls):
        cls.num += 1 
    def __init__(self):
        self.add1()

实际调用的时候可以使用实例对象.类方法或者类名.类方法

记录的时候实际也是一个属性, 指向一个代码段

可以直接使用实例或者类.名字 = 方法这种方式进行动态添加一个一个方法, 但是这一种方式不会自动传入self参数, 可以使用types进行动态添加方法

  • 实例方法
import types

class Person (object):
    def __init__(self, name=None, age=None):
        self.name = name
        self.age = age

p = Person("jiao", 21)

def show_info(self):
    print("----info----")
 
p.show_info = types.MethodType(show_info, p) # 自动把这一个参数传进去
p.show_info()
  • 类方法/静态方法

这两个可以直接在加了修饰器以后添加

类对象

在python里面定义class的时候这一个定义也是一个对象, 这一个对象里面有类属性等信息, 还有各种方法的代码

创建的实例里面有一个参数__class__, 这一个参数指向这个类对象

实例调用方法的时候会使用这一个类对象, 用于节省空间, 避免代码重复

可以使用dir(实例对象)查看有哪一些方法, 属性

dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。如果参数包含方法__dir__(),该方法将被调用。如果参数不包含__dir__(),该方法将最大限度地收集参数信息。

变量特殊命名方法

xx: 共有变量

_xx:私有化属性和方法, 使用from module import *禁止导入, 类对象和子类可以使用

__xx: 无法在外部直接访问, 私有属性

__xx__用户名名空间的魔法对象和属性

xx_: 用于避免和python关键字冲突

property动态数据

使用实例对象.属性 = xxx的时候会自动调用一个方法, 一个属性使用的时候是动态生成的, 需要在使用的时候通过一个函数进行生成

在使用=进行赋值的时候也可以使用这一个函数进行数据的检查

装饰器使用

class Foo(object):
    def func(self):
        print("fun running...")
    
    @property
    def prop(self):
        print("prop running...")

foo_boj = Foo()
foo_boj.func()
foo_boj.prop			#这一个调用的时候没有()但是会执行这一个函数

这一个函数的返回值会作为最后的结果, 可以使用这一个进行产生动态的数据

新式类

class Foo(object):
    def __init__(self) -> None:
        self.aaa = 1

    @property
    def prop(self):
        print("prop running...")

    @prop.setter
    def prop(self, value):
        print("prop setting...")
        self._prop = value
    
    @prop.getter
    def prop(self):
        print("prop getting...")
        return self._prop
    
    @prop.deleter
    def prop(self):
        print("prop deleting...")
        del self._prop
    

foo_boj = Foo()

foo_boj.prop = 2
print(foo_boj.prop)
del foo_boj.prop
PS E:\JHY\python\2024-4-22> python -u "e:\JHY\python\2024-4-22\main.py"
prop setting...
prop getting...
2
prop deleting...

@prop.getter没有定义的时候会使用@property的返回值, 后面两个必须在@property后面使用, 这一个属性是必须有的

另一种调用方式

class Foo(object):
    def __init__(self):
        self.bar = "jiao"
    def get_bar(self):
        return self.bar

    def set_bar(self, value):
        self.bar = value
    
    BAR = property(get_bar, set_bar, None, "This is a bar")

obj = Foo()
result = obj.BAR
print(result) 
obj.BAR = "haoyang"
print(obj.BAR)
print(Foo.BAR.__doc__)

image-20240503112644197

第四个参数是一个字符串, 使用类名.属性.__doc__可以获取

PS E:\JHY\python\2024-4-22> python -u "e:\JHY\python\2024-4-22\main.py"
jiao
haoyang
This is a bar

这一个使用+=进行赋值的时候相当于obj.BAR = obj.BAR + value , 所以实际调用的时候会使用一次获取以及一次写入

  • 实际使用实例

Django框架里面的property里面属性使用的就是这一个

不足

如果有多个属性需要设置的时候, 这里面需要实现很多的函数

内建函数集合

__init__引入

创建的时候会调用的函数

class Person:
    def __init__(self, name, age):
        self.age = age
        self.name = name
    def print_info:
        print(self.name, self.age)

__str__打印

这一个函数的返回值是使用print函数打印这一个类的时候结果

__call__

如果使用一个类创建了一个对象, 直接使用这一个对象当函数调用会执行这一个函数

__slots__静态

添加这一个属性可以使得这一个类不能在运行的时候进行添加属性, 只可以使用声明了的属性

class Person(object):
    __slots__ = ("name", "age")

p = Person()
p.name = "John"
p.age = 20
print(p.name, p.age)
p.address = "henan"
print(p.address)
PS E:\JHY\python\2024-4-22> python -u "e:\JHY\python\2024-4-22\main.py"
John 20
Traceback (most recent call last):
File "e:\JHY\python\2024-4-22\main.py", line 8, in <module>
p.address = "henan"
AttributeError: 'Person' object has no attribute 'address'

这一个效果只在定义他的类里面有效, 不会进行继承

import math

class Person:
    # 初始化
    def __init__(self, name, age):
        self.age = age
        self.name = name

    def print_info(self):
        print(self.name, self.age)
	
    # print直接打印的时候
    def __str__(self):
        return f'{self.name} is {self.age} years old'
    
    def __repr__(self):
        return f'Person({self.name}, {self.age})'
    
    def __eq__(self, other):
        return self.age == other.age
    
    def __lt__(self, other):
        return self.age < other.age

    def __gt__(self, other):
        return self.age > other.age

    def __le__(self, other):
        return self.age <= other.age
    
    def __ge__(self, other):
        return self.age >= other.age
    
    def __ne__(self, other):
        return self.age != other.age
    
    def __add__(self, other):
        return self.age + other.age
    
    def __sub__(self, other):
        return self.age - other.age
    
    def __mul__(self, other):
        return self.age * other.age
    
    def __truediv__(self, other):
        return self.age / other.age
    
    def __floordiv__(self, other):
        return self.age // other.age
    
    def __mod__(self, other):
        return self.age % other.age
    
    def __pow__(self, other):
        return self.age ** other.age
    
    def __and__(self, other):
        return self.age & other.age
    
    def __or__(self, other):    
        return self.age | other.age
    
    def __xor__(self, other):
        return self.age ^ other.age
    
    def __lshift__(self, other):
        return self.age << other.age
    
    def __rshift__(self, other):
        return self.age >> other.age
    
    def __neg__(self):
        return -self.age
    
    def __pos__(self):
        return +self.age
    
    def __abs__(self):
        return abs(self.age)
    
    def __invert__(self):
        return ~self.age
    
    def __round__(self, n=0):
        return round(self.age, n)
    
    def __floor__(self):
        return math.floor(self.age)
    
    def __ceil__(self):
        return math.ceil(self.age)
    
    def __trunc__(self):
        return math.trunc(self.age)
    
    def __index__(self):
        return self.age
    
    def __len__(self):
        return len(self.name)
    
    def __contains__(self, item):
        return item in self.name
    
    def __getitem__(self, key):
        return self.name[key]
    
    def __setitem__(self, key, value):
        self.name[key] = value

    def __delitem__(self, key):
        del self.name[key]

    def __iter__(self):
        return iter(self.name)
    
    def __reversed__(self):
        return reversed(self.name)
    
    def __next__(self):
        return next(self.name)
    
    def __hash__(self):
        return hash(self.name)
    
    def __call__(self):
        return self.print_info()
    
    def __enter__(self):
        print('Entering')

    def __exit__(self, exc_type, exc_value, traceback):
        print('Exiting')

    def __del__(self):
        print('Deleting')

p1 = Person('John', 30)
p2 = Person('Jane', 25)

p1.print_info()

print(p1 == p2)
print(p1 < p2)
print(p1 > p2)
print(p1 <= p2)
print(p1 >= p2)
print(p1 != p2)
print(p1 + p2)
print(p1 - p2)
print(p1 * p2)
print(p1 / p2)
print(p1 // p2)
print(p1 % p2)
print(p1 ** p2)
print(p1 & p2)
print(p1 | p2)
print(p1 ^ p2)
print(p1 << p2)
print(p1 >> p2)
print(-p1)
print(+p1)
print(abs(p1))
print(~p1)
print(round(p1, 1))
print(math.floor(p1))
print(math.ceil(p1))
print(math.trunc(p1))
print(hash(p1))
p1()
with p1:
    pass
del p1
John 30
False
False
True
False
True
True
55
5
750
1.2
1
5
8472886094430000000000000000000000000
24
31
7
1006632960
0
-30
30
30
-31
30
30
30
30
2252686827583093840
John 30
Entering
Exiting
Deleting
Deleting
常用的属性说明触发
__init__构建初始化的时候初见实际赋值的时候调用, 初始化new创建的实例
__new__生成的实例时候创建实际时, 最先调用, 返回实例
__class__实例所在的类实例.__class__
__str__实例的字符串显示, 可读性print打印的时候显示
__repr__实例的字符串显示, 准确性print(repr(实例类))或print直接调用, 优先级比较高
__del__析构(删除的时候执行)del实例
__dict__实例的自定义属性vars(实例.__dict__), 类.__dict__的时候只打印实例属性
__doc__类文档, 子类不会继承help(类或实例), 函数会打印他的描述
__getattribute__属性访问拦截器访问实例属性的时候, 传入的参数是这一个参数的字符串
__bases__类的所有的父类构成的元素print(类名.__bases__)

常用属性

  • __module__: 查看他所在的模块(文件)
  • __class__: 这一个实例使用的类

类字典实现

  • __getitem__, __setitem__, __delitem__: 使用实例[索引]和的时候会调用这三个
class Foo(object):
    def __getitem__(self, key):
        print("__getitem__", key)

    def __setitem__(self, key, value):
        print("__setitem__", key, value)

    def __delitem__(self, key):
        print("__delitem__", key)

foo = Foo()
foo["bar"] = "baz"
del foo["bar"]
foo["bar"]
PS E:\JHY\python\2024-4-22> python -u "e:\JHY\python\2024-4-22\main.py"
__setitem__ bar baz
__delitem__ bar
__getitem__ bar

传递的时候是一个字符串

  • __getslice__, __setslice__, __delslice__: 在python2里面的实现切片

元类

一个特殊的类, 用于定义其他的类

元类(type)–>类(类对象)–>实例对象

类的创建也可以使用类似于闭包的方式

def choice_class(name):
    if name == "foo":
        class Foo(object):
            pass
        return Foo
    else:
        class Bar(object):
            pass
        return Bar

使用type创建类

type实际有两个功能, 一个是测试类型, 另一个是创建一个类

type(类名, 父类的名称组成的元组(可以为空), 包含属性的字典(名称和值))
c_j = type("JIAO", (object,), {"name":"jiao", "age":18})
print(type(c_j))

obj_j = c_j()
print(obj_j.name)
print(obj_j.age)
PS E:\JHY\python\2024-4-22> python -u "e:\JHY\python\2024-4-22\main.py"
<class 'type'>
jiao
18

元类的定义

函数type实际上就是一个元类, 是python里面用于创建所有类的元类, Python里面的所有的类都是一个对象

JIAO = type("JIAO", (), {})
print(dir(JIAO))
print(JIAO.__dict__)
print(JIAO.__bases__) # 查看这一个对象继承的类
print(JIAO.__class__) # 查看它构建这一个实例使用的类
PS E:\JHY\python\2024-4-22> python -u "e:\JHY\python\2024-4-22\main.py"
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 
'__weakref__']
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'JIAO' objects>, '__weakref__': <attribute '__weakref__' of 'JIAO' objects>, '__doc__': None}        
(<class 'object'>,)
<class 'type'>

str, int这一些数据实际使用也是一个type类生成的对象, type这一个类的父类是object

自己创建元类

class Foo(object, metaclass=xxxxx):

在定义一个类的时候没有指定这一个metaclass, 会默认使用type进行创建, 否则使用这一个参数指定的那一个函数进行创建

class UperAttrMetaClass(type):
    #
    def __new__(cls, clsname, bases, dct):
        new_attr = {}
        for name, value in dct.items():
            if not name.startswith('__'):
                new_attr[name.upper()] = value
            else:
                new_attr[name] = value
        return type(clsname, bases, new_attr)
    

class Foo(metaclass=UperAttrMetaClass):
    bar = 'bip'

print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))

f = Foo()
print(f.BAR)
PS E:\JHY\python\2024-4-22> python -u "e:\JHY\python\2024-4-22\main.py"
False
True
bip

创建这一个类里面的属性的时候都使用大写

class UperAttrMetaClass(type):
    # 这一个函数会在类被创建的时候被调用
    # clsname: 类的名字
    # bases: 类的基类
    # dct: 类的属性
    # 默认的实现是返回一个新的类
    # 这个新的类会将属性名转换为大写
    def __new__(cls, clsname, bases, dct):
        new_attr = {}
        for name, value in dct.items():
            if not name.startswith('__'):
                new_attr[name.upper()] = value
            else:
                new_attr[name] = value
        return type(clsname, bases, new_attr)
    

class Foo(metaclass=UperAttrMetaClass):
    bar = 'bip'

print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))

f = Foo()
print(f.BAR)

类定义的过程

python解释器遇到一个类的时候实际会进行一次调用, 为了知道实际有的属性以及方法, 之后把这些方法属性传递到元类type里面, 创建一个对象, 这个就是类对象

如果类里面有放在外面代码的话, 这些代码会执行

描述符对象

property的实现, @classmethod等的实现都可以使用描述符

在大型的项目里面对MySQL数据库的操作的传媒也可以使用这一个进行实现

__getattr__

这一个是在调用一个不存在的属性的时候调用, 访问的属性存在的时候不会调用这一个方法

class A:
    def __init__(self, name):
        self.name = name
    # def __getattribute__(self, item):
    #     if item == "name":
    #         return "haha"
    #     else:
    #         return super().__getattribute__(item)
    def __getattr__(self, item):
        print("error item %s" % item)

a = A("jiao")
print(a.age) # 默认访问这一个的时候会报错, 现在不会
PS E:\JHY\python\2024-4-22> python -u "e:\JHY\python\2024-4-22\main.py"
error item age
None

默认这一个会产生一个异常

__getattribute__

访问一个对象的时候会调用这一个函数, 实际使用的时候可以使用这一个函数进行拦截

class A:
    def __init__(self, name):
        self.name = name
    def __getattribute__(self, item):
        if item == "name":
            return "haha"
        else:
            return super().__getattribute__(item)

a = A("jiao")
print(a.name)
PS E:\JHY\python\2024-4-22> python -u "e:\JHY\python\2024-4-22\main.py"
haha

**注: **这两个同时出现的时候会调用__getattribute__

描述符

之前使用的@property这一个属性会使得一个函数的方法属于的类从function变为property

这个property实际就是一个描述符, 严格来说, 如果一个类里面有__get__, __set__, __delete__这三个属性里面的任意一个, 那么这一个类实际就是一个描述符对象

如果有一个类, 这一个类里面有类属性对应的是上面的描述符对象创建的实例对象, 这一个类属性就是一个描述符

class A:
    def __get__(self, instance, owner):
        print("---get---")

    def __set__(self, instance, value):
        print("---set---")

    def __delete__(self, instance):
        print("---delete---")

class B:
    a = A()     # 这一个是一个描述符

b = B()
print(b.a)      # 调用__get__方法, b为instance, B是owner
b.a = 100       # 调用__set__方法, b为instance, 100是value
del b.a         # 调用__delete__方法
PS E:\JHY\python\2024-4-22> python -u "e:\JHY\python\2024-4-22\main.py"
---get---
None        
---set---   
---delete---

实际property就是这一个方式实现的

如果直接使用B.a进行调用的时候, instance这一个是None, owner还是B

使用描述符的原因

python里面没有属性, 所以可以使用这一个进行类型检测, 使用的时候比

实际使用

实现数据监测
class NonNegative(object):
    def __init__(self, default):
        self.default = default # 没有某一个参数的时候返回值
        self.data = dict()

    def __get__(self, instance, owner):
        return self.data.get(instance, self.default)
    
    def __set__(self, instance, value):
        if value < 0:
            raise ValueError('value must be non-negative')
        self.data[instance] = value

class Order(object):
    price = NonNegative(0)
    quantity = NonNegative(1)

    def __init__(self, name, price, quantity):
        self.name = name
        self.price = price
        self.quantity = quantity

    def total(self):
        return self.price * self.quantity
    

order = Order('apple', 1, 10)
print(order.total())
order.price = 10
print(order.total())
order.price = -10 # ValueError: value must be non-negative
实现classmethed
class classmethed_new(object):
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        def new_func(*args, **kwargs):
            return self.func(owner, *args, **kwargs)
        return new_func # 这是一个闭包, 返回实际调用的函数
    
class A(object):
    M = 100
    def a(self):
        print('A.a')
    
    @classmethed_new
    def b(cls):		# 这里实际为b = classmethed_new(b)
        print('A.b')
        print(cls.M)


obj = A()
obj.a()
print('-'*20)
obj.b()   # 从b获取一个函数, 之后执行这一个函数, 这时候的参数会给new_func, 之后new_func调用self.func
实现惰性计算

惰性计算就是需要一个值, 这一个值不是提前准备的, 是啥时候需要啥时候计算的

class LazyPropety(object):
    def __init__(self, fun) -> None:
        self.fun = fun
    
    def __get__(self, instance, owner):
        print("Calling __get__")
        if instance is None:
            return self
        value = self.fun(instance)
        # setattr(instance, area, value) 把这一个函数的名字指向的对象改为这一个计算值
        setattr(instance, self.fun.__name__, value)
        return value


class ReadOnlyNumber(object):
    def __init__(self, value) -> None:
        self.value = value
    
    def __get__(self, instance, owner):
        return self.value
    
    def __set__(self, instance, value):
        raise AttributeError("Can't set attribute")
    

class Circle(object):
    pi = ReadOnlyNumber(3.14)

    def __init__(self, radius):
        self.radius = radius
    # 初始的时候area这一个类保存的是一个描述符
    @LazyPropety
    def area(self):
        print("Calculating area")
        return self.pi * self.radius * self.radius
    

print("1.----")
a = Circle(4)
print("2.----")
print(a.area)
print("3.----")
print(a.area)
print("4.----")

image-20240504202648657

__dict__作用

使用这一个可是查看一个类里面的已经定义的属性

在python里面所有的属性以及方式使用的都是字典的方式进行存储的, 实力对象里面只有这一个对象的私有属性, 方法以及类的文档在类属性里面

这一个实际调用print(对象.__dict__), 也可以使用vars(对象)这一个函数进行替代

数据在调用的时候找到的是一个普通的属性的时候会直接调用, 如果这一个属性是一个描述符则会调用他的__get__方法

在获取数据的时候会首先从自己的字典里面获取数据, 没有这一个数据的话查找父类的字典

class M:
    def __init__(self) -> None:
        self.x = 1

    def __get__(self, instance, owner):
        print('get')
        return self.x
    
    def __set__(self, instance, value):
        print('set')
        self.x = value
    
class AA:
    m = M()
    n = 2
    def __init__(self, score) -> None:
        self.score = score


a = AA(3)
print(a.m)
print("-" * 20)
print(type(a).__dict__["m"].__get__(a, AA)) # 这个和上面的那一个等价

数据描述符和非数据描述符

同时有__get____set__的时候这是一个数据描述符

只有一个__get__的时候是一个非数据的描述符

这两个区别是:

属性名和描述符的名字一样的时候, 访问这一个属性的时候, 如果这一个描述符是数据描述符, 就会优先访问这一个描述符, 不是的话访问这一个属性

class M:
    def __init__(self) -> None:
        self.x = 1

    def __get__(self, instance, owner):
        return self.x
    
    def __set__(self, instance, value):
        self.x = value
    
class N:
    def __init__(self) -> None:
        self.x = 1

    def __get__(self, instance, owner):
        return self.x

class AA(object):
    m = M()     # 资料描述符
    n = N()     # 非资料描述符

    def __init__(self, m, n) -> None:
        self.m = m # 属性m和描述符m同名, 调用的时候会发生冲突
        # 数据描述符优先级高, 不会创建一个新的m, 会给m描述符赋值
        self.n = n # 属性n和描述符n同名, 这一个会创建一个新的属性
    
aa = AA(2, 3)
print('-'*20)
print(aa.__dict__)
print(AA.__dict__)
print('-'*20)
print(aa.n)     # 这俩是不一样的, 这个调用属性
print(AA.n)	    # 描述符
print('-'*20)
print(aa.m)     # 这俩是一样的, 都是用的描述符     
print(AA.m)
PS E:\JHY\python\2024-4-22> python -u "e:\JHY\python\2024-4-22\main.py"
--------------------
{'n': 3}
{'__module__': '__main__', 'm': <__main__.M object at 0x0000015CB8287FD0>, 'n': <__main__.N object at 0x0000015CB8287CD0>, '__init__': <function AA.__init__ at 0x0000015CB82AB0A0>, '__dict__': <attribute '__dict__' of 'AA' objects>, '__weakref__': <attribute '__weakref__' of 'AA' objects>, '__doc__': None}
--------------------
3
1
--------------------
2
2

只读描述符

同时定义两个属性, 但是在调用__set__的时候会触发一个AttributeError, 引发一个异常

实际调用的细节

使用默认的__getattribute__的时候访问一个属性的时候会使用hasattr进行判断是不是有一个属性__get__如果有的话会使用这一个函数的返回值

如果重新这一个函数会使得描述符失效

没有这一个属性的时候调用__getattr__

注意

  1. 使用描述符的时候这一个必须是一个类属性, 不能是实例属性, 否则的话这一个属性调用的时候不会调用这一个属性的方法
  2. 由于使用的是类属性, 所以这一个描述符在实现的时候需要区分不同的实例, 这个时候可以使用instance做一个字典的key

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1649445.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[leetcode] 67. 二进制求和

文章目录 题目描述解题方法模拟java代码复杂度分析 相似题目 题目描述 给你两个二进制字符串 a 和 b &#xff0c;以二进制字符串的形式返回它们的和。 示例 1&#xff1a; 输入:a "11", b "1" 输出&#xff1a;"100"示例 2&#xff1a; 输…

QML 本地存储(Setting,sqlite)

Qt hello - 专注于Qt的技术分享平台 QML 原生的储存方有两种&#xff1a; 1&#xff0c;Settings 跟QWidget 中的QSettings 一样&#xff0c;可以简单的存储一些配置。 2&#xff0c;Sqlite sqlite数据库。可以存储一些复杂的数据。 一&#xff0c;Settings 我们以一个按钮的位…

自动语音识别

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

学习笔记:【QC】Android Q qmi扩展nvReadItem/nvWriteItem

一、qmi初始化 流程图 初始化流程: 1、主入口&#xff1a; vendor/qcom/proprietary/qcril-hal/qcrild/qcrild/rild.c int main(int argc, char **argv) { const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **); rilInit RIL_Init; funcs rilInit…

深度学习之基于Matlab神经网络的活体人脸和视频人脸识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 人脸识别技术作为生物识别技术的一种&#xff0c;近年来得到了广泛的关注和应用。与传统的身份认证方…

小程序预览或上传代码时,遇到app.json未找到某个wxml文件的解决方法

uniapp小程序&#xff0c;点击预览或者是上传代码&#xff0c;遇到app.json无法找到某个wxml文件的解决方法&#xff1a;清缓存 问题&#xff1a; message&#xff1a;Error: app.json: 未找到 ["subPackages"][3]["pages"][3] 对应的 subPackages4/pages/…

RTT潘多拉开发板上实现电源管理

简介 随着物联网(IoT)的兴起&#xff0c;产品对功耗的需求越来越强烈。作为数据采集的传感器节点通常需要在电池供电时长期工作&#xff0c;而作为联网的SOC也需要有快速的响应功能和较低的功耗。 在产品开发的起始阶段&#xff0c;首先考虑是尽快完成产品的功能开发。在产品…

【负载均衡在线OJ项目日记】编译与日志功能开发

目录 日志功能开发 常见的日志等级 日志功能代码 编译功能开发 创建子进程和程序替换 重定向 编译功能代码 日志功能开发 日志在软件开发和运维中起着至关重要的作用&#xff0c;目前我们不谈运维只谈软件开发&#xff1b;日志最大的作用就是用于故障排查和调试&#x…

B端UX/UI设计面试作品集分层源文件figmasketch模板

当您考虑找工作时&#xff0c;是否曾质疑过项目复盘作品集的重要性&#xff1f;实际上&#xff0c;一份精心准备的项目复盘作品集对于求职者来说具有无可估量的价值&#xff0c;特别是对于设计师这一职业领域。 以下所述或许对您而言已非陌生。您的作品集应当成为您专业技能与…

系统架构设计师错题集

在实时操作系统中&#xff0c;两个任务并发执行&#xff0c;一个任务要等待另一个任务发来消息&#xff0c;或建立某个条件后再向前执行&#xff0c;这种制约性合作关系被称为任务的&#xff08;9&#xff09;。 (9)A.同步 B.互斥 C.调度 D.执行 【答案】A 【解析】本题考查…

安装docker20.10.18版本步骤

安装docker20.10.18版本步骤 准备低版本安装包 #安装20.10.18版本的dockercd /opt #切换目录#上传需要的docker20.10.18.zip安装包unzip docker20.10.18.zip #解压cd docker20.10.18/ #切换目录yum install -y *.rpm #安装systemctl enable --now docker.service #开机自启并…

手动配置dns后网速变慢

之前因为自动的dns能上qq但打不开网页&#xff0c;就手动设置了一个&#xff0c;结果近些天时不时出现网页图片加载慢的问题&#xff0c;影响到我看美女图片了&#xff0c;是可忍熟不可忍 测了下网速&#xff0c;很快&#xff0c;下载上传都是三位数的&#xff0c;那显然不是网…

【c++算法篇】双指针(下)

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;算法笔记仓 朋友们大家好啊&#xff0c;本篇文章我们来到算法的双指针的第二部分 目录 1.有效三角形的个数2.查找总价格为目标值的两个商品3.三数之和4.四数之和5.双指针常见场景总结 1.有效三角形…

mvc 异步请求、异步连接、异步表单

》》》 利用Jquery ajax 》》》 mvc 异步表单 c# MVC 添加异步 jquery.unobtrusive-ajax.min.js 方法 具–>Nuget程序包管理器–>程序包管理器控制台 在控制台输入&#xff1a;PM>Install-Package Microsoft.jQuery.Unobtrusive.Ajax –version 3.0.0 回车执行即可在…

【Diffusion实战】训练一个类别引导diffusion模型(Pytorch代码详解)

又学习了一种方法&#xff0c;类别引导diffusion模型&#xff0c;使用mnist数据集&#xff0c;记录一下它的用法吧。 Diffusion实战篇&#xff1a;   【Diffusion实战】训练一个diffusion模型生成S曲线&#xff08;Pytorch代码详解&#xff09;   【Diffusion实战】训练一个…

如何处理多模态数据噪声不均衡动态?天大等最新《低质量数据的多模态融合》综述

多模态融合致力于整合来自多种模态的信息&#xff0c;目的是实现更准确的预测。在包括自动驾驶和医疗诊断等广泛的场景中&#xff0c;多模态融合已取得显著进展。然而&#xff0c;在低质量数据环境下&#xff0c;多模态融合的可靠性大部分仍未被探索。本文综述了开放多模态融合…

Linux专栏03:使用Xshell远程连接云服务器

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Linux专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 使用Xshell远程连接云服务器 编号&#xff1a;03 文章目录 使用Xsh…

鸿蒙OpenHarmony实战开发-MiniCanvas

介绍 基于OpenHarmony的Cavas组件封装了一版极简操作的MiniCanvas&#xff0c;屏蔽了原有Canvas内部复杂的调用流程&#xff0c;支持一个API就可以实现相应的绘制能力&#xff0c;该库还在继续完善中&#xff0c;也欢迎PR。 使用说明 1.添加MiniCanvas依赖 在项目entry目录…

Spring Boot3.x集成Disruptor4.0

Disruptor介绍 Disruptor是一个高性能内存队列&#xff0c;研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与I/O操作处于同样的数量级)。基于Disruptor开发的系统单线程能支撑每秒600万订单&#xff0c;2010年在QCon演讲后&#xff0c;获得了业界关注。2011年&…

C++手写协程项目(协程实现线程结构体、线程调度器定义,线程挂起函数、线程切换函数、线程恢复函数、线程结束函数、线程结束判断函数,模块测试)

协程结构体定义 之前我们使用linux下协程函数实现了线程切换&#xff0c;使用的是ucontext_t结构体&#xff0c;和基于这个结构体的四个函数。现在我们要用这些工具来实现我们自己的一个线程结构体&#xff0c;并实现线程调度和线程切换、挂起。 首先我们来实现以下线程结构体…