# 多行结果输出支持
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"

"""
Explanation: 类与对象
End of explanation
"""


_formats = {
    'ymd' : '{d.year}-{d.month}-{d.day}',
    'mdy' : '{d.month}/{d.day}/{d.year}',
    'dmy' : '{d.day}/{d.month}/{d.year}'
    }

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    def __format__(self, code):
        if code == '':
            code = 'ymd'
        fmt = _formats[code]
        return fmt.format(d=self)

d = Date(2012, 12, 21)
d
format(d)
format(d, 'mdy')
'The date is {:ymd}'.format(d)
'The date is {:mdy}'.format(d)

"""
Explanation: 自定义字符串的格式化

为了自定义字符串的格式化，我们需要在类上面定义 format() 方法
End of explanation
"""


class A:
    def __init__(self):
        self._internal = 0 # An internal attribute
        self.public = 1 # A public attribute
    
#     @classmethod
    def public_method(self):
        '''
        A public method
        '''
        print(2)

    def _internal_method(self):
       print(3)

a = A()
a.public
# Python不会阻止对内部属性或者方法的访问，但是并不建议这么做
a._internal
a.public_method()
a._internal_method()

class B:
    def __init__(self):
        self.__private = 0

    def __private_method(self):
        pass

    def public_method(self):
        pass
        self.__private_method()

"""
Explanation: 让对象支持上下文管理协议

为了让一个对象兼容 with 语句，你需要实现 __enter__() 和 __exit__() 方法
编写上下文管理器的主要原理是你的代码会放到 with 语句块中执行。 当出现 with 语句的时候，对象的 __enter__() 方法被触发， 它返回的值(如果有的话)会被赋值给 as 声明的变量。然后，with 语句块里面的代码开始执行。 最后，__exit__() 方法被触发进行清理工作
不管 with 代码块中发生什么，上面的控制流都会执行完，就算代码块中发生了异常也是一样的

在类中封装属性名

Python不去依赖语言特性去封装数据，而是通过遵循一定的属性和方法命名规约来达到这个效果。
任何以单下划线_开头的名字都应该是内部实现（私有的）
Python并不会真的阻止别人访问内部名称。但是如果你这么做肯定是不好的，可能会导致脆弱的代码
使用下划线开头的约定同样适用于模块名和模块级别函数
使用两个下划线(__)开头的命名表示private，会被重新命名
有时候你定义的一个变量和某个保留关键字冲突，这时候可以使用单下划线作为后缀
End of explanation
"""


class C(B):
    def __init__(self):
        super().__init__()
        self.__private = 1 # Does not override B.__private

    # Does not override B.__private_method()
    def __private_method(self):
        pass

"""
Explanation: 使用双下划线开始会导致访问名称变成其他形式。 比如，在前面的类B中，私有属性会被分别重命名为 _B__private 和 _B__private_method 。 这时候你可能会问这样重命名的目的是什么，答案就是继承——这种属性通过继承是无法被覆盖的
End of explanation
"""


# 为了避免关键字之间的冲突问题
lambda_ = 2.0 # Trailing _ to avoid clash with lambda keyword

"""
Explanation: 这里，私有名称 __private 和 __private_method 被重命名为 _C__private 和 _C__private_method ，这个跟父类B中的名称是完全不同的
End of explanation
"""


class Person:
    def __init__(self, first_name):
        self.first_name = first_name

    # Getter function
    @property
    def first_name(self):
        return self._first_name

    # Setter function
    @first_name.setter
    def first_name(self, value):
        if not isinstance(value, str):
            raise TypeError('Expected a string')
        self._first_name = value

    # Deleter function (optional)
    @first_name.deleter
    def first_name(self):
        raise AttributeError("Can't delete attribute")

"""
Explanation: 创建可管理的属性

给某个实例attribute增加除访问与修改之外的其他处理逻辑，比如类型检查或合法性验证
自定义某个属性的一种简单方法是将它定义为一个property
End of explanation
"""


a = Person('autuanliu')
a.first_name

# 设置的时候会执行一个类型检查的方法
a.first_name = 43

a.first_name = "autuan"

del a.first_name

"""
Explanation: 上述代码中有三个相关联的方法，这三个方法的名字都必须一样
第一个方法是一个 getter 函数，它使得 first_name 成为一个属性。 其他两个方法给 first_name 属性添加了 setter 和 deleter 函数。 需要强调的是只有在 first_name 属性被创建后， 后面的两个装饰器 @first_name.setter 和 @first_name.deleter 才能被定义
property的一个关键特征是它看上去跟普通的attribute没什么两样， 但是访问它的时候会自动触发 getter 、setter 和 deleter 方法
End of explanation
"""


import math
class Circle:
    def __init__(self, radius):
        self.radius = radius

    @property
    def area(self):
        return math.pi * self.radius ** 2

    @property
    def diameter(self):
        return self.radius * 2

    @property
    def perimeter(self):
        return 2 * math.pi * self.radius

"""
Explanation: Properties还是一种定义动态计算attribute的方法。 这种类型的attributes并不会被实际的存储，而是在需要的时候计算出来
可以把 方法 变为 属性，这样在执行构造函数之后，就可以按照访问属性的方式进行访问方法了，而只有在访问的时候才会被计算出来
End of explanation
"""


# 实例化一个圆的对象
xy = Circle(5.2)

# 可以直接通过属性的方式进行访问
xy.area
xy.diameter
xy.perimeter

"""
Explanation: 通过使用properties，将所有的访问接口形式统一起来， 对半径、直径、周长和面积的访问都是通过属性访问，就跟访问简单的attribute是一样的。 如果不这样做的话，那么就要在代码中混合使用简单属性访问和方法调用
End of explanation
"""


class A:
    def spam(self):
        print('A.spam')

class B(A):
    def spam(self):
        print('B.spam')
        super().spam()

b = B()

b.spam()

class A:
    def __init__(self):
        self.x = 0

class B(A):
    def __init__(self):
        super().__init__()
        self.y = 1

b = B()
b.x
b.y

"""
Explanation: 调用父类方法

想在子类中调用父类的某个已经被覆盖的方法,可以使用 super() 函数
super() 函数的一个常见用法是在 init() 方法中确保父类被正确的初始化了
super() 的另外一个常见用法出现在覆盖Python特殊方法的代码中
End of explanation
"""


from abc import ABCMeta, abstractmethod

# 抽象类
class IStream(metaclass=ABCMeta):
    @abstractmethod
    def read(self, maxbytes=-1):
        pass

    @abstractmethod
    def write(self, data):
        pass

# 抽象类不能直接被实例化
a = IStream()

class ss(IStream):
    def read(self, maxbytes=-1):
        print('read')
    
    def write(self, data):
        print('write')

aa = ss()
aa.read()
aa.write(data=1)

# @abstractmethod 还能注解静态方法、类方法和 properties 。 你只需保证这个注解紧靠在函数定义前即可
class A(metaclass=ABCMeta):
    @property
    @abstractmethod
    def name(self):
        pass

    @name.setter
    @abstractmethod
    def name(self, value):
        pass

    @classmethod
    @abstractmethod
    def method1(cls):
        pass

    @staticmethod
    @abstractmethod
    def method2():
        pass

# 静态方法实例
import math
class Circle1:
    def __init__(self, radius):
        self.radius = radius

    @staticmethod
    def area(self):
        return math.pi * self.radius ** 2

aaa = Circle1(3)
Circle1.area(aaa)

"""
Explanation: 使用延迟计算属性

想将一个只读属性定义成一个property，并且只在访问的时候才会计算结果。 但是一旦被访问后，你希望结果值被缓存起来，不用每次都去计算
定义一个延迟属性的一种高效方法是通过使用一个描述器类

简化数据结构的初始化

你写了很多仅仅用作数据结构的类，不想写太多烦人的 __init__() 函数, 可以在一个基类中写一个公用的 __init__() 函数, r然后继承这个基类

定义接口或者抽象基类

你想定义一个接口或抽象类，并且通过执行类型检查来确保子类实现了某些特定的方法， 使用 abc 模块可以很轻松的定义抽象基类
抽象类的一个特点是它不能直接被实例化
抽象类的目的就是让别的类继承它并实现特定的抽象方法
@abstractmethod 还能注解静态方法、类方法和 properties 。 你只需保证这个注解紧靠在函数定义前即可
End of explanation
"""


