2021-10-19

WARNING: This article may be obsolete
This post was published in 2021-10-19. Obviously, expired content is less useful to users if it has already pasted its expiration date.
This article is categorized as "Garbage" . It should NEVER be appeared in your search engine's results.



Python dunder methods/magic methods

🔗 [Python中的魔术方法(Dunder or magic methods) - 简书] https://www.jianshu.com/p/cfcec753b8cd

在Python中,有一类名字前后有双下划线做前缀和后缀的方法,例如:__ init __, __ add __, __ len __, __ repr __ 。这类方法叫做魔术方法Magic method,通常用于重载Python内建方法和运算(overload Python’s built-in methods and its operators)。更Pythonic的叫法是:Dunder method -- “Double Under (Underscores)”。

https://www.jianshu.com/p/cfcec753b8cd

__repr__()应该如何理解?应该理解为 representation 

https://docs.python.org/3/library/functions.html#repr

补充:🔗 [python - How to print instances of a class using print()? - Stack Overflow] https://stackoverflow.com/questions/1535327/how-to-print-instances-of-a-class-using-print

在这个stackoverflow的提问里,有类似这样一段代码:

class Test:
    def __init__(self):
        self.a = "foo"
        self.b = "bar"


t = Test()
print(t)

# <__main__.Test object at 0x7fca19726e20>

本意是想要打印一些有用的东西,比如object t里面的所有attributes,显然这个时候不能直接print(t)

解决方法1:使用print(vars(t)),但此时只能打印所有变量,如果我有什么其他要求呢?(比如按照某种格式打印部分变量)

所以解决方法2为:重写__repr__(self)方法:

class Test:
    def __init__(self):
        self.a = "foo"
        self.b = "bar"

    def __repr__(self):
        return "a value: %s, b value: %s" % (self.a, self.b)


t = Test()
print(t)

# a value: foo, b value: bar

另一个需要注意的是__repr__和__str__一起使用时的效果:

class demo:
    def __repr__(self):
        return 'call repr'

    def __str__(self):
        return 'call str'


d = demo()
print(repr(d))
print(d)

结果:

call repr
call str

如果在class里重写了 __repr__() ,但又想调用python原本的 __repr__() ,那么可以使用 object.__repr__(obj) (同理其他dunder methods),如下:

参考:[python - How can we get the default behavior of __repr__()? - Stack Overflow] https://stackoverflow.com/questions/48777014/how-can-we-get-the-default-behavior-of-repr

class demo:
    def __repr__(self):
        return 'call repr'

    def __str__(self):
        return 'call str'


d = demo()
print(repr(d))
print(d)
print(object.__repr__(d))

结果:

call repr
call str
<__main__.demo object at 0x7ffaa4377c10>

但是仍然要注意:

class demo:
    def __repr__(self):
        return 'call repr'

    def __str__(self):
        return 'call str'


d = demo()
print(repr(d))
print(d)
print(object.__repr__(d))
print(str(d))
print(object.__str__(d))

结果:

call repr
call str
<__main__.demo object at 0x7f952ca97c50>
call str
call repr

注意上面代码的第14行,执行 object.__str__(d) 会涉及到 __repr__() ,是因为:

https://stackoverflow.com/questions/12448175/confused-about-str-on-list-in-python

同时,stackoverflow上面还有一个热门问题:🔗 [python - What is the difference between __str__ and __repr__? - Stack Overflow] https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr

在这个问题里,有一个答案:https://stackoverflow.com/a/1436756

可以引申出这个话题:【python创建新对象】,单独拉出来作为一节目录:

Python创建新对象

class demo:
    pass


d = demo()
print(d)
e = demo()
print(e)
print(demo())
print(demo())
print(id(demo()))
print(id(demo()))

结果:

<__main__.demo object at 0x7fefc4185210>
<__main__.demo object at 0x7fefc41d2b90>
<__main__.demo object at 0x7fefc4397c90>
<__main__.demo object at 0x7fefc4397c90>
140667766013072
140667766013072

看起来两个(不进行赋值操作的) demo()  objects拥有相同的地址和id,那么它们是同一个object吗?( demo() is demo() 会返回True吗?)

不是的:

class demo:
    pass


d = demo()
print(d)
e = demo()
print(e)
print(demo())
print(demo())
print(id(demo()))
print(id(demo()))
print(demo() is demo())

结果:

<__main__.demo object at 0x7fcfb967d210>
<__main__.demo object at 0x7fcfb96cabd0>
<__main__.demo object at 0x7fcfbb297c90>
<__main__.demo object at 0x7fcfbb297c90>
140530175016080
140530175016080
False

具体解释可以见:🔗 [How can two Python objects have same id but 'is' operator returns False? - Stack Overflow] https://stackoverflow.com/questions/50893267/how-can-two-python-objects-have-same-id-but-is-operator-returns-false

大概意思就是说:在 demo() is demo() 里,前面的demo()创建了一个新的对象,但是因为没有创建对应的reference,所以立刻被销毁了;后面的demo()创建的是另一个新的对象,只是使用了相同的地址。

为了验证前面的猜想(两个生命周期不重叠的对象拥有相同的ID)不是偶然,可以加大剂量:

class demo:
    pass


for i in range(0, 100000):
    if id(demo()) != id(demo()):
        print('Find two different IDs')
        exit()
    a = demo()
    b = demo()
    c = demo()
    d = demo()
print('not found')

结果:

not found

针对"different object with same id"还有其他更深入的技巧,见:🔗 [How to make two objects have the same id in python? - Stack Overflow] https://stackoverflow.com/questions/16977196/how-to-make-two-objects-have-the-same-id-in-python


Python 继承

2021-10-19原版笔记

快速入门教程:🔗 [继承和多态 - 廖雪峰的官方网站] https://www.liaoxuefeng.com/wiki/1016959663602400/1017497232674368#0

代码(同时参考了🔗 [python - How to call super method from grandchild class? - Stack Overflow] https://stackoverflow.com/questions/31232098/how-to-call-super-method-from-grandchild-class)

class A:
    def call(self):
        print('A')


class B(A):
    def call(self):
        print('B')


class C(B):
    def call(self):
        super().call()
        A.call(self)
        super(B, self).call()


if __name__ == '__main__':
    C().call()

结果:

B
A
A

如果修改class B的代码如下:

class A:
    def call(self):
        print('A')


class B(A):
    # def call(self):
    #     print('B')
    pass


class C(B):
    def call(self):
        super().call()
        A.call(self)
        super(B, self).call()


if __name__ == '__main__':
    C().call()

则结果为:

A
A
A

2024-01-15新增

实际写代码的时候遇到了一些新的需求:想快速把父类转换为子类,但又不想写那么多代码(因为子类仅仅比父类多几个特殊属性),怎么办?

比如:class Vehicle:一堆属性,速度、坐标、制造商、路线计划、行驶里程,等等

现在我临时要加个class Bus,拥有class Vehicle的所有属性,自己仅仅是多了几个特殊属性

但当前的一堆代码都是操作class Vehicle的,比如move(v: Vehicle),比如shortest_path(v: Vehicle),而且class Vehicle里面的属性特别多,根本不想写又臭又长的代码把属性一个一个迁移到Bus object里面去

核心代码: **vars(vehicle_obj) 

from copy import deepcopy


class Vehicle:
    def __init__(self):
        self.uid = None
        self.speed = None
        self.plate = None
        self.manufacturer = None
        self.coordinate_x = None
        self.coordinate_y = None

    def assign_attributes(self, uid, speed, plate, manufacturer, coordinate_x, coordinate_y):
        self.uid = uid
        self.speed = speed
        self.plate = plate
        self.manufacturer = manufacturer
        self.coordinate_x = coordinate_x
        self.coordinate_y = coordinate_y


class Bus(Vehicle):
    def __init__(self, vehicle_obj, bus_capacity):
        super().__init__()
        # 这是shallow copy
        # self.assign_attributes(**vars(vehicle_obj))
        self.assign_attributes(**deepcopy(vars(vehicle_obj)))  # 这是deepcopy
        self.bus_capacity = bus_capacity


def vehicle_move(v: Vehicle):
    # 假设这是一个原本写的方法,现在不想修改它
    print('move %s' % v.__class__.__name__)
    v.coordinate_x += 1
    v.coordinate_y += 1


v = Vehicle()
v.assign_attributes(uid=12345, speed=10, plate="AAAAAA", manufacturer="Tesla", coordinate_x=99, coordinate_y=99)
vehicle_move(v)
# 临时增加一个新的class Bus
b_v = Bus(v, bus_capacity=30)
vehicle_move(b_v)
print(vars(v))
print(vars(b_v))

运行结果:

move Vehicle
move Bus
{'uid': 12345, 'speed': 10, 'plate': 'AAAAAA', 'manufacturer': 'Tesla', 'coordinate_x': 100, 'coordinate_y': 100}
{'uid': 12345, 'speed': 10, 'plate': 'AAAAAA', 'manufacturer': 'Tesla', 'coordinate_x': 101, 'coordinate_y': 101, 'bus_capacity': 30}

最后打印v和b_v变量的时候可以看到,b_v和v的坐标是不同的,说明b_v被复制创造出来以后,vehicle_move(b_v)确实只移动了b_v,而没有移动v ,事实上,代码里提到的两种复制变量的语句都能做到这一点:

self.assign_attributes(**vars(vehicle_obj))
self.assign_attributes(**deepcopy(vars(vehicle_obj)))

但deepcopy显然能把这件事情做得更彻底,使用deepcopy创造出b_v以后,b_v和v不会具有任何联系,可以放心大胆的修改它们,而无需担心reference to object这样的东西暗中作祟。

有关deepcopy的笔记:🔗 [2021-03-24 - Truxton's blog] https://truxton2blog.com/2021-03-24/

如果一定要看看不使用deepcopy的后果:

下面这个代码没有使用deepcopy,并尝试在b_v被复制出来以后修改v的属性,结果发现b_v的属性也被一带修改了:

from copy import deepcopy


class Vehicle:
    def __init__(self):
        self.uid = None
        self.speed = None
        self.plate = None
        self.manufacturer = None
        self.coordinate_x = None
        self.coordinate_y = None

    def assign_attributes(self, uid, speed, plate, manufacturer, coordinate_x, coordinate_y, routing_plan):
        self.uid = uid
        self.speed = speed
        self.plate = plate
        self.manufacturer = manufacturer
        self.coordinate_x = coordinate_x
        self.coordinate_y = coordinate_y
        self.routing_plan = routing_plan


class Bus(Vehicle):
    def __init__(self, vehicle_obj, bus_capacity):
        super().__init__()
        # 这是shallow copy
        self.assign_attributes(**vars(vehicle_obj))
        # self.assign_attributes(**deepcopy(vars(vehicle_obj)))  # 这是deepcopy
        self.bus_capacity = bus_capacity


def vehicle_move(v: Vehicle):
    # 假设这是一个原本写的方法,现在不想修改它
    print('move %s' % v.__class__.__name__)
    v.coordinate_x += 1
    v.coordinate_y += 1


v = Vehicle()
v.assign_attributes(uid=12345, speed=10, plate="AAAAAA", manufacturer="Tesla", coordinate_x=99, coordinate_y=99,
                    routing_plan=[0, 1, 2, 3, [4, 5, 6]])
vehicle_move(v)
# 临时增加一个新的class Bus
b_v = Bus(v, bus_capacity=30)
vehicle_move(b_v)
del v.routing_plan[-1] # 新增了这一行
print(vars(v))
print(vars(b_v))

运行结果:

move Vehicle
move Bus
{'uid': 12345, 'speed': 10, 'plate': 'AAAAAA', 'manufacturer': 'Tesla', 'coordinate_x': 100, 'coordinate_y': 100, 'routing_plan': [0, 1, 2, 3]}
{'uid': 12345, 'speed': 10, 'plate': 'AAAAAA', 'manufacturer': 'Tesla', 'coordinate_x': 101, 'coordinate_y': 101, 'routing_plan': [0, 1, 2, 3], 'bus_capacity': 30}

发现 del v.routing_plan[-1] 这句话也会把b_v变量里的routing_plan一带改变了。这就不是我们想要的结果。


重新复习一遍merge sort的性质

参考🔗 [2021-10-17 - Truxton's blog] https://truxton2blog.com/2021-10-17/ , 🔗 [2021-10-18 - Truxton's blog] https://truxton2blog.com/2021-10-18/

worst casebest caseaverage
merge sort[mathjax]n\log(n)[/mathjax][mathjax]n\log(n)[/mathjax][mathjax]n\log(n)[/mathjax]

各大主流排序算法的时间复杂度

资料查找来源:

Insertion sort 🔗 [Insertion sort - Wikipedia] https://en.wikipedia.org/wiki/Insertion_sort

quicksort:🔗 [2021-10-18 - Truxton's blog] https://truxton2blog.com/2021-10-18/

heap sort: 🔗 [2021-10-18 - Truxton's blog] https://truxton2blog.com/2021-10-18/ (搜索"主定理")🔗 [Heapsort - Wikipedia] https://en.wikipedia.org/wiki/Heapsort

merge sort:🔗 [2021-10-17 - Truxton's blog] https://truxton2blog.com/2021-10-17/ ,🔗 [2021-10-18 - Truxton's blog] https://truxton2blog.com/2021-10-18/

bucket sort: 📚 见中文算法导论p103

radix sort: 来源于中文算法导论和网络资源(wikipedia, geekforgeeks.org)

Best CaseWorst CaseAverage
bubble sort[mathjax]O(n)[/mathjax][mathjax]O(n^2)[/mathjax][mathjax]O(n^2)[/mathjax]
selection sort[mathjax]O(n^2)[/mathjax][mathjax]O(n^2)[/mathjax][mathjax]O(n^2)[/mathjax]
Insertion sort[mathjax]O(n)[/mathjax][mathjax]O(n^2)[/mathjax][mathjax]O(n^2)[/mathjax]
quicksort[mathjax]O(n\log(n))[/mathjax][mathjax]O(n^2)[/mathjax][mathjax]O(n\log(n))[/mathjax]
randomized quicksort[mathjax]O(n\log(n))[/mathjax][mathjax]O(n\log(n))[/mathjax][mathjax]O(n\log(n))[/mathjax]
heap sort[mathjax]O(n\log(n))[/mathjax][mathjax]O(n\log(n))[/mathjax][mathjax]O(n\log(n))[/mathjax]
merge sort[mathjax]O(n\log(n))[/mathjax][mathjax]O(n\log(n))[/mathjax][mathjax]O(n\log(n))[/mathjax]
bucket sort (+ insertion sort)[mathjax]O(n)[/mathjax][mathjax]O(n^2)[/mathjax][mathjax]O(n)[/mathjax]
radix sort[mathjax]O(nk)[/mathjax], k: max number of digits[mathjax]O(nk)[/mathjax], k: max number of digits[mathjax]O(nk)[/mathjax], k: max number of digits
counting sort[mathjax]O(n+k)[/mathjax], k: range of inputs[mathjax]O(n+k)[/mathjax], k: range of inputs[mathjax]O(n+k)[/mathjax], k: range of inputs

Python语法杂项

python数组的[:]切片

>>> a=[1,2,3,4]
>>> a[:]
[1, 2, 3, 4]
>>> a[:-1]
[1, 2, 3]
>>> a[::-1]
[4, 3, 2, 1]
>>> a[::-2]
[4, 2]
>>> a[:2:1]
[1, 2]
>>> a[:1:-1]
[4, 3]

Python: mutable和immutable

https://medium.com/@meghamohan/mutable-and-immutable-side-of-python-c2145cf72747

补充:这张表可以用来记忆 python的参数传递机制 ,具体见: 🔗 [2022-02-07 - Truxton's blog] https://truxton2blog.com/2022-02-07/#Python函数的参数传递机制


Python set, list, tuple的区别

🔗 [python中set、list与tuple的区别 - 简书] https://www.jianshu.com/p/10db00d9a682

来自https://towardsdatascience.com/15-examples-to-master-python-lists-vs-sets-vs-tuples-d4ffb291cf07

Python type hints

见:🔗 [typing — Support for type hints — Python 3.10.0 documentation] https://docs.python.org/3/library/typing.html


没有完成的内容

有关[mathjax]F(n)=F(n-1)+O(2^n)[/mathjax]的时间复杂度推导

(鸽了)



 Last Modified in 2024-01-15 

Leave a Comment Anonymous comment is allowed / 允许匿名评论