像计算机科学家一样学习python(10)

面向对象的编程(1)

Python学习笔记-像计算机科学家一样学习python(10)

类和对象

Python
import math

class Point:
    """二维上的一个点
    属性: x, y"""

def distance_between_points(p1, p2):
    """计算;两点间的距离"""
    l = math.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2) 
    return l

if __name__ == "__main__":
    p1 = Point()
    p2 = Point()
    p1.x = 1
    p1.y = 1
    p2.x = 2
    p2.y = 2
    print(distance_between_points(p1, p2))

15.5

Python
import math

class Point:
    """二维上的一个点
    属性: x, y"""

class Rectangle:
    """二维上的一个点
    属性: x, y, corner(左下角顶点)"""

def move_rectangle(Rectangle, dx, dy):
    Rectangle.x = Rectangle.x + dx
    Rectangle.y = Rectangle.y + dy

15.6

Python
def move_rectangle(Rectangle, dx, dy):
    rect2 = copy.deepcopy(Rectangle)
    rect2.x = rect2.x + dx
    rect2.y = rect2.y + dy

练习 15-1

Python
import math
import copy


class Point:
    """二维上的一个点
    属性: x, y"""


class Circle:
    """二维上的一个圆
    属性: center, radius"""


class Rectangle:
    """二维上的一个点
    属性: x, y, corner(左下角顶点)"""


def distance_between_points(p1, p2):
    """计算;两点间的距离"""
    l = math.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2) 
    return l


def point_in_circle(Circle, Point):
    l = distance_between_points(Point, Circle.center)
    if l <= Circle.radius:
        return True


def print_point(p):
    """Print a Point object in human-readable format."""
    print('(%g, %g)' % (p.x, p.y))


def rect_in_circle(rect, circle):
    """Checks whether the corners of a rect fall in/on a circle.

    rect: Rectangle object
    circle: Circle object
    """
    p = copy.copy(rect.corner)
    print_point(p)
    if not point_in_circle(p, circle):
        return False

    p.x += rect.width
    print_point(p)
    if not point_in_circle(p, circle):
        return False

    p.y -= rect.height
    print_point(p)
    if not point_in_circle(p, circle):
        return False

    p.x -= rect.width
    print_point(p)
    if not point_in_circle(p, circle):
        return False

    return True

c = Circle()
p = Point()
p.x = 150
p.y = 100
c.center = p
c.radius = 75
print(point_in_circle(c, p))

15-2

Python
import turtle

from Point1 import Point, Rectangle
from Circle import Circle

import polygon

def draw_circle(t, circle):
    """Draws a circle.

    t: Turtle
    circle: Circle
    """
    t.pu()
    t.goto(circle.center.x, circle.center.y)
    t.fd(circle.radius)
    t.lt(90)
    t.pd()
    polygon.circle(t, circle.radius)


def draw_rect(t, rect):
    """Draws a rectangle.

    t: Turtle
    rect: Rectangle
    """
    t.pu()
    t.goto(rect.corner.x, rect.corner.y)
    t.setheading(0)
    t.pd()

    for length in rect.width, rect.height, rect.width, rect.height:
        t.fd(length)
        t.rt(90)


if __name__ == '__main__':
    bob = turtle.Turtle()

    # draw the axes
    length = 400
    bob.fd(length)
    bob.bk(length)
    bob.lt(90)
    bob.fd(length)
    bob.bk(length)

    # draw a rectangle
    box = Rectangle()
    box.width = 100.0
    box.height = 200.0
    box.corner = Point()
    box.corner.x = 50.0
    box.corner.y = 50.0

    draw_rect(bob, box)

    # draw a circle
    circle = Circle
    circle.center = Point()
    circle.center.x = 150.0
    circle.center.y = 100.0
    circle.radius = 75.0

    draw_circle(bob, circle)

    # wait for the user to close the window
    turtle.mainloop()

类和函数

Python
class Time:
    """下略
    """

time = Time()
time.hour = 11
time.minute = 59
time.second = 30

def print_time(time):
    print('%.2d:%.2d:%.2d' %(time.hour, time.minute, time.second))

print_time(time)

16.3

Python
def increment(time, seconds):
    """时间进位
    time: Time()对象
    seconds: 秒数"""
    minutes = (seconds + time.second) // 60
    time.second = (seconds + time.second) % 60
    time.hour += (minutes + time.minute) //60
    time.minute = (minutes + time.minute) % 60
    return time
Python
def increment(time, seconds):
    """时间进位
    time: Time()对象
    seconds: 秒数"""
    time_2 = Time()
    time_2.hour = 0
    time_2.minute = 0
    time_2.second = 0
    minutes = (seconds + time.second) // 60
    time_2.second = (seconds + time.second) % 60
    time_2.hour = time.hour + (minutes + time.minute) //60
    time_2.minute = (minutes + time.minute) % 60
    return time_2

练习

16-2

1

Python
from datetime import date

print(date.isoweekday(date.today()))

2

Python
print("Your next birthday and how far away it is:")
    #s = input('Enter your birthday in mm/dd/yyyy format: ')
    s = '5/11/1967'
    bday = datetime.strptime(s, '%m/%d/%Y')

    next_bday = bday.replace(year=today.year)
    if next_bday < today:
        next_bday = next_bday.replace(year=today.year+1)
    print(next_bday)

    until_next_bday = next_bday - today
    print(until_next_bday)

    print("Your current age:")
    last_bday = next_bday.replace(year=next_bday.year-1)
    age = last_bday.year - bday.year
    print(age)

3

Python
    print("For people born on these dates:")
    bday1 = datetime(day=11, month=5, year=1967)
    bday2 = datetime(day=11, month=10, year=2003)
    print(bday1)
    print(bday2)

    print("Double Day is")
    d1 = min(bday1, bday2)
    d2 = max(bday1, bday2)
    dd = d2 + (d2 - d1)
    print(dd)

4

Python
    print("n Day is")
    n = 3
    dd_2 = abs(d1 -d2)
    nd = max(bday1, bday2) + dd_2/(n-1)
    print(nd)

类和方法

Python
class Time:
    """下略
    """
    def time_to_int(self):
        minutes = self.hour * 60 + self.minutes
        seconds = minutes * 60 + self.seconds
        return seconds

17.5

Python
class Point:
    """下略
    """
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y
        
    def __str__(self):
        return '(%g,%g)' % (self.x, self.y)    

17.7

查看文档:特殊方法

3.3. 特殊方法名称

一个类可以通过定义具有特殊名称的方法来实现由特殊语法所引发的特定操作 (例如算术运算或下标与切片)。这是 Python 实现 操作符重载 的方式,允许每个类自行定义基于操作符的特定行为。例如,如果一个类定义了名为 __getitem__() 的方法,并且 x 为该类的一个实例,则 x[i] 基本就等同于 type(x).__getitem__(x, i)。除非有说明例外情况,在没有定义适当方法的情况下尝试执行一种操作将引发一个异常 (通常为 AttributeErrorTypeError)。

将一个特殊方法设为 None 表示对应的操作不可用。例如,如果一个类将 __iter__() 设为 None,则该类就是不可迭代的,因此对其实例调用 iter() 将引发一个 TypeError (而不会回退至 __getitem__()). 2

在实现模拟任何内置类型的类时,很重要的一点是模拟的实现程度对于被模拟对象来说应当是有意义的。例如,提取单个元素的操作对于某些序列来说是适宜的,但提取切片可能就没有意义。(这种情况的一个实例是 W3C 的文档对象模型中的 NodeList 接口。)

3.3.1. 基本定制

  • object.__new__(cls[, ...])

    调用以创建一个 cls 类的新实例。__new__() 是一个静态方法 (因为是特例所以你不需要显式地声明),它会将所请求实例所属的类作为第一个参数。其余的参数会被传递给对象构造器表达式 (对类的调用)。__new__() 的返回值应为新对象实例 (通常是 cls 的实例)。典型的实现会附带适宜的参数使用 super().__new__(cls[, ...]),通过超类的 __new__() 方法来创建一个类的新实例,然后根据需要修改新创建的实例再将其返回。如果 __new__() 在构造对象期间被发起调用并且它返回了一个实例或 cls 的子类,则新实例的 __init__() 方法将以 __init__(self[, ...]) 的形式被发起调用,其中 self 为新实例而其余的参数与被传给对象构造器的参数相同。如果 __new__() 未返回一个 cls 的实例,则新实例的 __init__() 方法就不会被执行。__new__() 的目的主要是允许不可变类型的子类 (例如 int, str 或 tuple) 定制实例创建过程。它也常会在自定义元类中被重载以便定制类创建过程。

  • object.__init__(self[, ...])

    在实例 (通过 __new__()) 被创建之后,返回调用者之前调用。其参数与传递给类构造器表达式的参数相同。一个基类如果有 __init__() 方法,则其所派生的类如果也有 __init__() 方法,就必须显式地调用它以确保实例基类部分的正确初始化;例如: super().__init__([args...]).因为对象是由 __new__()__init__() 协作构造完成的 (由 __new__() 创建,并由 __init__() 定制),所以 __init__() 返回的值只能是 None,否则会在运行时引发 TypeError

  • object.__del__(self)

    在实例将被销毁时调用。这还会调用终结器或析构器 (不适当)。如果一个基类具有 __del__() 方法,则其所派生的类如果也有 __del__() 方法,就必须显式地调用它以确保实例基类部分的正确清除。__del__() 方法可以 (但不推荐!) 通过创建一个该实例的新引用来推迟其销毁。这被称为对象 重生__del__()是否会在重生的对象将被销毁时再次被调用是由具体实现决定的 ;当前的 CPython 实现只会调用一次。当解释器退出时不会确保为仍然存在的对象调用 __del__() 方法。注解 del x 并不直接调用 x.__del__() --- 前者会将 x 的引用计数减一,而后者仅会在 x 的引用计数变为零时被调用。CPython implementation detail: It is possible for a reference cycle to prevent the reference count of an object from going to zero. In this case, the cycle will be later detected and deleted by the cyclic garbage collector. A common cause of reference cycles is when an exception has been caught in a local variable. The frame's locals then reference the exception, which references its own traceback, which references the locals of all frames caught in the traceback.参见 gc 模块的文档。警告 由于调用 __del__() 方法时周边状况已不确定,在其执行期间发生的异常将被忽略,改为打印一个警告到 sys.stderr。特别地:__del__() 可在任意代码被执行时启用,包括来自任意线程的代码。如果 __del__() 需要接受锁或启用其他阻塞资源,可能会发生死锁,例如该资源已被为执行 __del__() 而中断的代码所获取。__del__() 可以在解释器关闭阶段被执行。因此,它需要访问的全局变量(包含其他模块)可能已被删除或设为 None。Python 会保证先删除模块中名称以单个下划线打头的全局变量再删除其他全局变量;如果已不存在其他对此类全局变量的引用,这有助于确保导入的模块在 __del__() 方法被调用时仍然可用。

  • object.__repr__(self)

    repr() 内置函数调用以输出一个对象的“官方”字符串表示。如果可能,这应类似一个有效的 Python 表达式,能被用来重建具有相同取值的对象(只要有适当的环境)。如果这不可能,则应返回形式如 <...someuseful description...> 的字符串。返回值必须是一个字符串对象。如果一个类定义了 __repr__() 但未定义 __str__(),则在需要该类的实例的“非正式”字符串表示时也会使用 __repr__()。此方法通常被用于调试,因此确保其表示的内容包含丰富信息且无歧义是很重要的。

  • object.__str__(self)

    通过 str(object) 以及内置函数 format()print() 调用以生成一个对象的“非正式”或格式良好的字符串表示。返回值必须为一个 字符串 对象。此方法与 object.__repr__() 的不同点在于 __str__() 并不预期返回一个有效的 Python 表达式:可以使用更方便或更准确的描述信息。内置类型 object 所定义的默认实现会调用 object.__repr__()

  • object.__bytes__(self)

    通过 bytes 调用以生成一个对象的字节串表示。这应该返回一个 bytes 对象。

  • object.__format__(self, format_spec)

    通过 format() 内置函数、扩展、格式化字符串字面值 的求值以及 str.format() 方法调用以生成一个对象的“格式化”字符串表示。 format_spec 参数为包含所需格式选项描述的字符串。 format_spec 参数的解读是由实现 __format__() 的类型决定的,不过大多数类或是将格式化委托给某个内置类型,或是使用相似的格式化选项语法。请参看 格式规格迷你语言 了解标准格式化语法的描述。返回值必须为一个字符串对象。在 3.4 版更改: object 本身的 format 方法如果被传入任何非空字符,将会引发一个 TypeError在 3.7 版更改: object.__format__(x, '') 现在等同于 str(x) 而不再是 format(str(self), '')

  • object.__lt__(self, other)

  • object.__le__(self, other)

  • object.__eq__(self, other)

  • object.__ne__(self, other)

  • object.__gt__(self, other)

  • object.__ge__(self, other)

    以上这些被称为“富比较”方法。运算符号与方法名称的对应关系如下:x 调用 x.lt(y)x<=y调用x.le(y)x==y调用x.eq(y)x!=y调用x.ne(y)x>y调用x.gt(y)x>=y调用x.ge(y)。如果指定的参数对没有相应的实现,富比较方法可能会返回单例对象 NotImplemented。按照惯例,成功的比较会返回 FalseTrue。不过实际上这些方法可以返回任意值,因此如果比较运算符是要用于布尔值判断(例如作为 if 语句的条件),Python 会对返回值调用 [bool()](https://docs.python.org/zh-cn/3/library/functions.html#bool) 以确定结果为真还是假。在默认情况下 [ne()](https://docs.python.org/zh-cn/3/reference/datamodel.html#object.__ne__) 会委托给 [eq()](https://docs.python.org/zh-cn/3/reference/datamodel.html#object.__eq__) 并将结果取反,除非结果为 NotImplemented。比较运算符之间没有其他隐含关系,例如 (x 为真并不意味着 x<=y。要根据单根运算自动生成排序操作,请参看 functools.total_ordering()。请查看 __hash__() 的相关段落,了解创建可支持自定义比较运算并可用作字典键的 hashable 对象时要注意的一些事项。这些方法并没有对调参数版本(在左边参数不支持该操作但右边参数支持时使用);而是 __lt__()__gt__() 互为对方的反射, __le__()__ge__() 互为对方的反射,而 __eq__()__ne__() 则是它们自己的反射。如果两个操作数的类型不同,且右操作数类型是左操作数类型的直接或间接子类,则优先选择右操作数的反射方法,否则优先选择左操作数的方法。虚拟子类不会被考虑。

  • object.__hash__(self)

    通过内置函数 hash() 调用以对哈希集的成员进行操作,属于哈希集的类型包括 setfrozenset 以及 dict__hash__() 应该返回一个整数。对象比较结果相同所需的唯一特征属性是其具有相同的哈希值;建议的做法是把参与比较的对象全部组件的哈希值混在一起,即将它们打包为一个元组并对该元组做哈希运算。例如:def __hash__(self): return hash((self.name, self.nick, self.color)) 注解 hash() 会从一个对象自定义的 __hash__() 方法返回值中截断为 Py_ssize_t 的大小。通常对 64 位构建为 8 字节,对 32 位构建为 4 字节。如果一个对象的 __hash__() 必须在不同位大小的构建上进行互操作,请确保检查全部所支持构建的宽度。做到这一点的简单方法是使用 python -c "import sys;print(sys.hash_info.width)"。如果一个类没有定义 __eq__() 方法,那么也不应该定义 __hash__() 操作;如果它定义了 __eq__() 但没有定义 __hash__(),则其实例将不可被用作可哈希集的项。如果一个类定义了可变对象并实现了 __eq__() 方法,则不应该实现 __hash__(),因为可哈希集的实现要求键的哈希集是不可变的(如果对象的哈希值发生改变,它将处于错误的哈希桶中)。用户定义的类默认带有 __eq__()__hash__() 方法;使用它们与任何对象(自己除外)比较必定不相等,并且 x.__hash__() 会返回一个恰当的值以确保 x == y 同时意味着 x is yhash(x) == hash(y)。一个类如果重载了 __eq__() 且没有定义 __hash__() 则会将其 __hash__() 隐式地设为 None。当一个类的 __hash__() 方法为 None 时,该类的实例将在一个程序尝试获取其哈希值时正确地引发 TypeError,并会在检测 isinstance(obj, collections.abc.Hashable) 时被正确地识别为不可哈希对象。如果一个重载了 __eq__() 的类需要保留来自父类的 __hash__() 实现,则必须通过设置 __hash__ =.__hash__ 来显式地告知解释器。如果一个没有重载 __eq__() 的类需要去掉哈希支持,则应该在类定义中包含 __hash__ = None。一个自定义了 __hash__() 以显式地引发 TypeError 的类会被 isinstance(obj, collections.abc.Hashable) 调用错误地识别为可哈希对象。注解 在默认情况下,str 和 bytes 对象的 __hash__() 值会使用一个不可预知的随机值“加盐”。 虽然它们在一个单独 Python 进程中会保持不变,但它们的值在重复运行的 Python 间是不可预测的。这种做法是为了防止以下形式的拒绝服务攻击:通过仔细选择输入来利用字典插入操作在最坏情况下的执行效率即 O(n^2) 复杂度。详情见 http://www.ocert.org/advisories/ocert-2011-003.html改变哈希值会影响集合的迭代次序。Python 也从不保证这个次序不会被改变(通常它在 32 位和 64 位构建上是不一致的)。另见 PYTHONHASHSEED.在 3.3 版更改: 默认启用哈希随机化。

  • object.__bool__(self)

    调用此方法以实现真值检测以及内置的 bool() 操作;应该返回 FalseTrue。如果未定义此方法,则会查找并调用 __len__() 并在其返回非零值时视对象的逻辑值为真。如果一个类既未定义 __len__() 也未定义 __bool__() 则视其所有实例的逻辑值为真。

Python
    def __add__(self, other):
        self.x = self.x + other.x
        self.y = self.y + other.y
        return self

17.8

Python
class Point:
    """下略
    """
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __str__(self):
        return '(%g,%g)' % (self.x, self.y)

    def __add__(self, other):
        if isinstance(other, Point):
            return self.add_point(other)
        else:
            return self.increment(other)
    
    def add_point(self, other):
        p = Point()
        p.x = self.x + other.x
        p.y = self.y + other.y
        return p

    def increment(self,other):
        p = Point()
        p.x = other[0] + self.x
        p.y = other[1] + self.y
        return p

        

p1 = Point(1, 1)
p2 = (2, 2)
print(p1 + p2)

练习

17-1

Python
class Time:
    """Represents the time of day.
       
    attributes: hour, minute, second
    """
    def __init__(self, hour=0, minute=0, second=0):
        """Initializes a time object.

        hour: int
        minute: int
        second: int or float
        """
        minutes = hour * 60 + minute
        self.seconds = minutes * 60 + second

    def __str__(self):
        """Returns a string representation of the time."""
        minutes, second = divmod(self.seconds, 60)
        hour, minute = divmod(minutes, 60)
        return '%.2d:%.2d:%.2d' % (hour, minute, second)

    def print_time(self):
        """Prints a string representation of the time."""
        print(str(self))

    def time_to_int(self):
        """Computes the number of seconds since midnight."""
        return self.seconds

    def is_after(self, other):
        """Returns True 

17-2

Python
class Kangaroo:
    """我是一个袋鼠
    """

    def __init__(self, name, pounch_contents = None):
        """将袋鼠对象的属性归零

        pouch_contents: 列表
        """    
        self.name = name
        if pounch_contents == None:
            pounch_contents = []
        self.pouch_contents = pounch_contents


    def put_in_pouch(self, other):
        """将一个元素放进列表中
        
        other: 任意数据类型"""
        self.pouch_contents.append(other)


    def __str__(self):
        """打印袋鼠对象"""
        t = [self.name + ' has pouch contents:']
        for obj in self.pouch_contents:
            s = '    ' + obj.__str__()
            t.append(s)
        return '\n'.join(t)



kanga = Kangaroo('Kanga',['bla'])
roo = Kangaroo('Roo',['blabla'])
kanga.put_in_pouch('wallet')
kanga.put_in_pouch('car keys')
roo.put_in_pouch('air')
kanga.put_in_pouch(roo)
print(kanga)
print(roo)

所以请不要在参数用可变对象当默认值

最后编辑于
文章链接: http://pheustal.com/2019/12-22/thinkpython10
本作品采用CC-BY-SA许可。