深圳幻海软件技术有限公司 欢迎您!

七 个 Python 问题,来扫扫盲

2023-02-28

这7个问题,我是有收获的,整理如下:1、反射算术运算符你可能知道Python里面的魔法函数,比如 __add__​ 和 __sub__​ 代表+-运算符,表示obj+/-something,但你可能不知道还有一个 __radd__,__rsub__&

这 7 个问题,我是有收获的,整理如下:

1、反射算术运算符

你可能知道 Python 里面的魔法函数,比如 __add__​ 和 __sub__​ 代表 + - 运算符,表示 obj +/- something,但你可能不知道还有一个 __radd__,__rsub__ 函数,可以表示 something +/- obj。

举例如下:

class Dog:
    def __add__(self, other):
        return "from __add__"
    def __radd__(self, other):
        return "from __radd__"
dog = Dog()
print(dog + 1)   # from __add__
print(1 + dog)   # from __radd__
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

2、__getattr__ vs __getattribute__

__getattr__​ 魔术方法只有在我们试图获取不存在的属性时才会被调用,__getattribute__ 在每次我们尝试访问属性时都会被调用。

代码如下:

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __getattr__(self, key):
        return f"{key} not found"
dog = Dog("taidi", 5)
print(dog.name)    # taidi
print(dog.age)     # 5
print(dog.breed)   # breed not found
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __getattribute__(self, key):
        return f"{key} not found"
dog = Dog("taidi", 5)
print(dog.name)    # name not found
print(dog.age)     # age not found
print(dog.breed)   # breed not found
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

3、super().__init__() 的另一种写法

class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age
class Dog(Animal):
    def __init__(self, name, age, breed):
        super().__init__(name, age)
        self.breed = breed
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

等价于:

class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age
class Dog(Animal):
    def __init__(self, name, age, breed):
        Animal.__init__(self, name, age)
        self.breed = breed
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

请注意,Animal.__init__(self, name, age) 不能少了 self 参数。

4、检查子类的方法

class Animal: pass
class Dog(Animal): pass
class Cat(Animal): pass
class GermanSheperd(Dog): pass
print(Animal.__subclasses__())
# [<class '__main__.Dog'>, <class '__main__.Cat'>]
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

不过,.__subclasses__() 只能检查直接子类。

5、多重集成时,同名函数,子类用的是哪一个?

class A:
    def test(self):
        print("A")
class B:
    def test(self):
        print("B")
class C(A, B):
    pass

C().test()   # A
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

A 和 B 都有 test 方法,那么 C 到底集成了哪一个呢?在 Python 中,最左边的类优先。

在这里,A 是最左边的父类,因此 A 的 test 方法被集成。

多充继承让人困惑,不用为好。

6 __invert__ 魔法函数

class Dog:
    def __invert__(self):
        return "test"
dog = Dog()
print(~dog)   # test
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

~ 运算符代表“按位非”,通常用于反转内容。一个更有意义的例子如下:

class Coordinate:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __str__(self):
        return f"({self.x}, {self.y})"
    def __invert__(self):
        return Coordinate(-self.x, -self.y)
a = Coordinate(3, 4)
b = ~a
print(a, b)   # (3, 4) (-3, -4)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

7、不使用 class 来创建类

def init(self, name, age):
    self.name = name
    self.age = age
def bark(self):
    print("woof")
Dog = type("Dog", (), {"__init__":init, "bark":bark})


dog = Dog("taidi", 10)
print(dog.name)
print(dog.age)

# taidi
# 10
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

在这里,我们将 3 个参数传递给 type 以创建我们的类。

第一个参数 __name__​ 是类的名称 第二个参数 __bases__​ 是一个包含父类的元组 第三个参数 __dict__ 是一个包含属性和方法的字典。

等价于:

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def bark(self):
        print("woof")
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.