Python2

魔法方法

  • __init__()

    (1)__init__()方法:构造器,当一个实例被创建的时候调用的初始化方法。

  • __new__()

    (1)__new__(cls, [...]) 是在一个对象实例化的时候所调用的第一个方法,所以它才是真正意义上的构造方法。
    (2)它的第一个参数是这个类,其他的参数是用来直接传递给 __init__ 方法

  • __del__()

类的表示

__str__() / __repr__()

这两个方法都是用来描述类或对象信息的,比如你直接实例化了一个对象,打印出来的是这个对象的地址。而要是重新在类中定义了这两个方法,那打印对象的结果就是方法返回的信息。

__repr__

通常情况下,直接输出某个实例化对象,本意往往是想了解该对象的基本信息,例如该对象有哪些属性,它们的值各是多少等等。但默认情况下,我们得到的信息只会是“类名+object at+内存地址”,对我们了解该实例化对象帮助不大。

默认情况下,repr() 会返回和调用者有关的 “类名+object at+内存地址”信息。当然,我们还可以通过在类中重写这个方法,从而实现当输出实例化对象时,输出我们想要的信息。

class CLanguage:
    def __init__(self):
        self.name = "C语言中文网"
        self.add = "http://c.biancheng.net"
    def __repr__(self):
        return "CLanguage[name="+ self.name +",add=" + self.add +"]"
clangs = CLanguage()
print(clangs)

__str__

如果要把一个类的实例变成 str,就需要实现特殊方法__str__():

class Student(object):
    def __init__(self,id,name,age):
        self.id=id
        self.name=name
        self.age=age
 
    def __str__(self):
        return "学号:{}--姓名:{}--年龄{}".format(self.id,self.name,self.age)
    
s=Student(111,"Bob",18)
print(s)

__bool__()

当调用 bool(obj) 时,会调用 __bool__() 方法,返回 True 或 False:

class Person(object):
    def __init__(self, uid):
        self.uid = uid

    def __bool__(self):
        return self.uid > 10

p1 = Person(4)
p2 = Person(14)
print(bool(p1))  # False
print(bool(p2))  # True
  • __eq__()
  • __ne__()
  • __lt__()
  • __gt__()

比较大小用的 和上面的一样

访问控制

__setattr__:定义当一个属性被设置时的行为
__getattr__:定义当用户试图获取一个不存在的属性时的行为
__delattr__:删除某个属性时调用
__getattribute__:访问任意属性或方法时调用

7、容器类操作(重要)

__setitem__(self, key, value):定义设置容器中指定元素的行为,相当于 self[key] = value;

__getitem__(self, key): 定义获取容器中指定元素的行为,相当于 self[key];

__delitem__(self, key):定义删除容器中指定元素的行为,相当于 del self[key];

__len__(self):定义当被 len() 调用时的行为(返回容器中元素的个数);

__iter__(self):定义当迭代容器中的元素的行为;

__contains__(self, item):定义当使用成员测试运算符(in 或 not in)时的行为;

__reversed__(self):定义当被 reversed() 调用时的行为。

Python 中常见的容器类型有:

  • 字典
  • 元组
  • 列表
  • 字符串

因为它们都是「可迭代」的。可迭代是因为,它们都实现了容器协议

class MyList(object):
    """自己实现一个list"""

    def __init__(self, values=None):
        # 初始化自定义list
        self.values = values or []
        self._index = 0

    def __setitem__(self, key, value):
        # 添加元素
        self.values[key] = value

    def __getitem__(self, key):
        # 获取元素
        return self.values[key]

    def __delitem__(self, key):
        # 删除元素
        del self.values[key]

    def __len__(self):
        # 自定义list的元素个数
        return len(self.values)

    def __iter__(self):
        # 可迭代
        return self

    def __next__(self):
        # 迭代的具体细节
        # 如果__iter__返回self 则必须实现此方法
        if self._index >= len(self.values):
            raise StopIteration()
        value = self.values[self._index]
        self._index += 1
        return value

    def __contains__(self, key):
        # 元素是否在自定义list中
        return key in self.values

    def __reversed__(self):
        # 反转
        return list(reversed(self.values))

# 初始化自定义list
my_list = MyList([1, 2, 3, 4, 5])

print(my_list[0])	     # __getitem__
my_list[1] = 20		     # __setitem__

print(1 in my_list)	     # __contains__
print(len(my_list))     # __len__

print([i for i in my_list])  # __iter__
del my_list[0]	             # __del__

reversed_list = reversed(my_list) # __reversed__
print([i for i in reversed_list])  # __iter__

这个例子实现了一个 MyList 类,在这个类中,定义了很多容器类的魔法方法。这样一来,这个 MyList 类就可以像操作普通 list 一样,通过切片的方式添加、获取、删除、迭代元素了。

__call__(self, [args...])

序列化

Python 提供了序列化模块 pickle,当使用这个模块序列化一个实例化对象时,也可以通过魔法方法来实现自己的逻辑,这些魔法方法包括:

  • __getstate__()
  • __setstate__()

pickle.dumps() 是 Python 中的一个函数,用于将 Python 对象序列化为一个字节流,以便将其保存到文件、传输到网络或在进程之间进行通信。

以下是一个使用 pickle.dumps() 的示例:

import pickle

data = {'name': 'Alice', 'age': 25, 'is_student': True}

# 将数据序列化为字节流
bytes_data = pickle.dumps(data)

# 输出字节流长度和内容
print(len(bytes_data))
print(bytes_data)
output:
b'\x80\x04\x95*\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x05Alice\x94\x8c\x03age\x94K\x19\x8c\nis_student\x94\x88u.'

在这个例子中,我们先定义了一个包含三个键值对的字典 data。然后,我们使用 pickle.dumps() 函数将该字典序列化为一个字节流,并将结果存储在变量 bytes_data 中。最后,我们输出了字节流的长度和内容。

需要注意的是,使用 pickle 序列化和反序列化对象时,必须保证两者使用相同的协议和版本。否则可能会导致不兼容的问题。此外,在序列化敏感信息(例如密码)等时应格外小心,以免泄漏敏感信息。

import pickle

class Person(object):

    def __init__(self, name, age, birthday):
        self.name = name
        self.age = age
        self.birthday = birthday

    def __getstate__(self):
        # 执行 pick.dumps 时 忽略 age 属性
        return {
            'name': self.name,
            'birthday': self.birthday
        }

    def __setstate__(self, state):
        # 执行 pick.loads 时 忽略 age 属性
        self.name = state['name']
        self.birthday = state['birthday']

person = Person('李四', 20, (2017, 2, 23))
pickled_person = pickle.dumps(person) # 自动执行 __getstate__ 方法

p = pickle.loads(pickled_person) # 自动执行 __setstate__ 方法
print(p.name, p.birthday)  # 李四 (2017, 2, 23)
# 由于执行 pick.loads 时 忽略 age 属性,所以下面执行回报错
print(p.age)  # AttributeError: 'Person' object has no attribute 'age'

面向对象

继承

子类如果重写了__init__方法,子类就不会自动继承父类__init__中的属性。如果要继承父类的属性,需要用到super方法,我们在B类的__init__方法中加上:

super(子类,self).__init__(参数0,参数1...)
父类名称.__init__(self,参数0,参数1...)
复制代码

若继承父类的所有属性就直接用super().__init__()

  • 在调用基类方法时,需加上基类的类名前缀,并带上self参数变量。但在类中调用普通函数不需要带上self函数。

多态和封装略

面向对象的各种方法

1、静态方法 (用这个装饰器来表示 @staticmethod )

@staticmethod 下面的函数就不属于这个类了,只不是还是要通过类名的方式调用 .变成了类方法

@staticmethod 定义的静态方法不需要访问实例和类属性,因此其参数列表中不需要包含 selfcls 参数。而 @classmethod 定义的类方法必须接受一个 cls 参数作为第一个参数,该参数指代调用该方法的类本身。

因为self.name这个变量是实例化这个类传进去的,类方法是不能访问实例变量的,只能访问类里面定义的变量

正确示例: 
class Person(object):
    def __init__(self, name):
        self.name = name
 
    @staticmethod  # 把eat方法变为静态方法
    def eat(x):
        print("%s is eating" % x)
 
 
d = Person("xiaoming")
d.eat("jack")   
 Person.eat("jack")
#就把eat方法当作一个独立的函数给他传参就行了 不能传入name

2.类方法@classmethod

类方法只能访问类变量,不能访问实例变量

错误示例: 
 
class Person(object):
    def __init__(self, name):
        self.name = name
 
    @classmethod  # 把eat方法变为类方法
    def eat(self):
        print("%s is eating" % self.name)
 
 
d = Person("xiaoming")
d.eat()  
 
###########   
 
结果: 
AttributeError: type object 'Person' has no attribute 'name'
因为self.name这个变量是实例化这个类传进去的,类方法是不能访问实例变量的,只能访问类里面定义的变量   


正确的如下
class Person(object):
    name="杰克"
    def __init__(self, name):
        self.name = name
 
    @classmethod  # 把eat方法变为类方法
    def eat(self):
        print("%s is eating" % self.name)
 
 
d = Person("xiaoming")
d.eat()
  • 定义在类中的变量叫类变量、方法之外的变量。与实例变量不同,类变量被所有实例共享,并且可以通过类名或实例名来访问。

  • 实例变量在 Python 中,实例变量是定义在类的方法中,以 self. 开头的变量。与类变量不同,实例变量只能在实例内部访问和修改,并且每个实例都有自己的一组实例变量。

    以下是一个使用实例变量的示例:

    在这个例子中,我们定义了一个名为 MyClass 的类,并在类的构造方法中定义了两个实例变量 xy。我们还定义了一个名为 add_xy() 的实例方法,该方法将两个实例变量相加并返回结果。在主程序中,我们创建了两个 MyClass 的实例 obj1obj2,并分别调用它们的 add_xy() 方法。由于 obj1xy 分别是 35,因此其 add_xy() 方法返回的结果是 8。而 obj2xy 分别是 79,因此其 add_xy() 方法返回的结果是 16。最后,我们通过修改实例变量的值来测试 add_xy() 方法是否正确地计算了结果。

    class MyClass:
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
        def add_xy(self):
            return self.x + self.y
    
    obj1 = MyClass(3, 5)
    obj2 = MyClass(7, 9)
    
    print(obj1.add_xy())   # 输出 8
    print(obj2.add_xy())   # 输出 16
    

3、属性方法 (用这个装饰器表示 @property)

把一个方法变成一个静态属性,属性就不用加小括号那样的去调用了

因为eat此时已经变成一个属性了, 不是方法了, 想调用已经不需要加()号了,直接d.eat就可以了

class Person(object):
 
    def __init__(self, name):
        self.name = name
 
    @property  # 把eat方法变为属性方法
    def eat(self):
        print("%s is eating" % self.name)
 
 
d = Person("xiaoming")
d.eat
posted @ 2023-03-23 17:35  ZZX11  阅读(21)  评论(0编辑  收藏  举报