Python 面向对象进阶

构造方法、析构方法、str 方法

构造方法
class Student:

    def __init__(self, name, height, weigth, money,likes):
        self.name = name
        self.height = height
        self.weigth = weigth
        self.money = money
        self.likes = likes
        # self.likes = "睡觉"  # 没有通过参数传入的,而是设置一个默认值,表示只要实例化对象,内部就会有一个爱好:睡觉
		# 即是 人人都爱睡觉

		# self.变量名 这个变量名可以随便取,self.变量名 可以把它当做是在实例对象内封装了一个初始容器

# 定义个行为(方法)吃饭
def eat(self):
    print("%s在吃饭" % self.name)  # 不同对象所对应的属性,所以是self.属性名


# 创建一个有初始化数据的对象
# 格式: 对象名 = 类名(多个值)
list1 = [1, 2, 3]
student1 = Student("pgnoe", 160, 180, 3000, list1)
# student2 = Student()错误写法,参数个数不一致
student2 = Student("李小璐", 160, 160, 20000, list1)

# 取值
print(student1.height)
print(student2.height)
student1.eat()  # 结果:pgone在吃饭

# 修改值
student1.height = 170


# 不可变类型的数据
print(student1.height)  # student1修改了值,发生改变
print(student2.height)

# 可变类型的数据
list1 = [1, 2, 3]
student1 = Student("pgnoe", 160, 180, 3000, list1)
student2 = Student("李小璐", 170, 160, 20000, list1)

student1.likes[0] = 100
print(student1.likes)
print(student2.likes)  # 结果:[100,2,3] [100,2,3] 都改变了

深入理解构造方法

class Person:

    def __init__(self, name, age, gender):  # 构造方法,如果 类名() ,则该方法会被自动执行
        self.n1 = name  # self就相当于一个容器,self.名字随便取,开辟空间,然后存放 参数/内容
        self.n2 = age
        self.n3 = gender

    def kc(self):  # 已经有三个空间了,而且存了内容
        data = "%s,性别%s,今年%s岁,喜欢上山砍柴" % (self.n1, self.n3, self.n2)
        print(data)

    def db(self):
        data = "%s,性别%s,今年%s岁,喜欢开车去东北" % (self.n1, self.n3, self.n2)
        print(data)

    def bj(self):
        data = "%s,性别%s,今年%s岁,喜欢大宝剑" % (self.n1, self.n3, self.n2)
        print(data)


obj = Person('老狗', 20, '男')  # 创建一个对象(实例化),构造方法就会被执行
# obj就是传给前面的self的
obj.kc()
obj.db()
obj.bj()


class Person():
    def __init__(self, name):
        self.name = name
        self.age = 19  # 已经使用默认值,上面的参数列表可以不用写参数


one = Person("min")  # 默认带2个值


# print(one.name,one.age) 结果:min 19 证明了,one=self


# 作用1:将数据封装到对象中,以供自己在方法中调用
class FileHandler:  # 读取一个文件
    def __init__(self, file_path):  # 通过构造方法来封装
        self.file_path = file_path
        self.f = open(self.file_path, 'rb')  # self. 就是一个封装过程
        # 将每次都要打开文件的操作,放在初始化的位置

    def read_first(self):
        self.f.read()
        # ...
        pass

    def read_last(self):
        # self.f.read()
        # ...
        pass

    def read_second(self):
        # self.f...
        # ...
        pass


obj = FileHandler('C:/xx/xx.log')
obj.read_first()
obj.read_last()
obj.read_second()
obj.f.close()


# 作用2:将数据封装到对象中,以供其他函数调用

def new_fun(arg):
    print(arg.k1)
    arg.k2
    arg.k3


class Foo:
    def __init__(self, k1, k2, k3):
        self.k1 = k1
        self.k2 = k2
        self.k3 = k3


rig = Foo(11, 22, 33)  # 将数据封装在rig对象中
new_fun(rig)
>>>应用案列:

class User_info:
    def __init__(self):
        self.name = None  # self提供一个容器,它的真正名字是self.后面的

    def info(self):
        print('当前用户名称:%s' % (self.name,))

    def account(self):
        print('当前用户%s的账单是:....' % (self.name,))

    def shopping(self):
        print('%s购买了一个人形抱枕' % (self.name,))

    def login(self):
        user = input('请输入用户名:')
        pwd = input('请输入密码:')
        if pwd == 'yep':
            self.name = user  # 让容器 中存放 user的值,self.name 表示读取值
            while True:
                print("""
                         1. 查看用户信息
                         2. 查看用户账单
                         3. 购买抱枕
                    """)
                num = int(input('请输入选择的序号:'))
                if num == 1:
                    self.info()  # 可以写作 obj.info()
                elif num == 2:
                    self.account()
                elif num == 3:
                    self.shopping()
                else:
                    print('序号不存在,请重新输入')

        else:
            print("登录失败")


obj = User_info()
obj.login()
析构方法
class Student(object):
    def __init__(self, name, height, weigth, money):
        self.name = name
        self.height = height
        self.weigth = weigth
        self.money = money
        print("这就是构造函数")

    # 定义个行为(方法)吃饭
    def eat(self):
        print("%s在吃饭" % self.name)
        
	def __del__(self):
		print('这是析构函数')

obj = Student('肖战',60,185,100000000)
 
obj.eat()

del obj
		

析构函数

__del__”就是一个析构函数了,当使用del 删除对象时,会调用他本身的析构函数,
另外当对象在某个作用域中调用完毕,在跳出其作用域的同时析构函数也会被调用一次,这样可以用来释放内存空间。  

del()也是可选的,如果不提供,则Python 会在后台提供默认析构函数

如果要显式的调用析构函数,可以使用del关键字: del obj

# 第一种:
# 当程序运行的完毕的时候,对象会自动的销毁
student1 = Student("pingone",160,180,138000)
     
# 第二种:手动的释放内存,即删除变量
del student1
while True:
     pass

# 第二种,在函数内
# 当一个变量是在函数中定义的,当这个函数执行完毕的时候,这个变量会自动的释放---建议代码写在函数中
def func():
     student1 = Student("胖胖",160,180,178000)
     print("调用了函数")
func()
while True:
     pass


# 和变量手动删除的区别:
num1 = 1
num2 = 1
num3 = 1
del num1 # 要全部删除三个,内存才会得到释放
str 方法
class Student(object):
    def __init__(self, name, height, weigth, money):
        self.name = name
        self.height = height
        self.weigth = weigth
        self.money = money


def eat(self):
    print("%s在吃饭" % self.name)

    # 当我们执行print方法打印该对象时,会 自动的调用__str__ 方法,方便程序员来查看值的
    # 如果一个对象的属性需要大量打印,可以重写__str__方法
    # 如果不写__str__函数,默认是打印对象的类型和内存地址


def __str__(self):
    return "name:%s,height:%d,weigth:%d,money:%d" % (self.name, self.height, self.weigth, self.money)


# 查看所有的数据
student1 = Student("林俊杰", 175, 160, 100000)
# print(student1.name,student2,money...)
# print("name:%s,height:%d,weigth:%d,money:%d"%(student1.name,student1.height,student1.weigth,student1.money))
print(student1)

student2 = Student("周杰伦", 170, 160, 100)
# print("name:%s,height:%d,weigth:%d,money:%d"%(student2.name,student2.height,student2.weigth,student2.money))
print(student2)

类的成员

字段(变量)

类的成员可以分为三大类:字段(变量)、方法和属性

在这里插入图片描述

所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。
而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。
类变量和实例变量(静态字段 和 字段)
class Foo:
    country = "中国"  # 类变量(静态字段)

    def __init__(self, name):
        self.name = name  # 实例变量(字段)

    def func(self):
        pass

# 准则:

# 实例变量(字段)访问时,使用对象访问,即: obj1.name

# 类变量(静态字段)访问时,使用类访问,即: Foo.country  (实在不方便时,才使用对象去访问)

obj1 = Foo('周杰伦')
obj2 = Foo('林俊杰')
print(obj1.name) # 周杰伦
print(Foo.country) # 中国

"""
obj1 = Foo('周杰伦')
obj2 = Foo('林俊杰')


# obj1.name = 'alex'
# print(obj1.name) # alex
# print(obj2.name) # 林俊杰


# obj1.country = '美国'
# print(obj1.country) # 美国
# print(obj2.country) # 中国


# Foo.country = '美国'
# print(obj1.country) # 美国
# print(obj2.country) # 美国
"""

在这里插入图片描述

什么时候用类变量?
当所有对象中有共同的字段时且要改都改要删都删时,
可以将 实例变量(字段) 提取到 类变量(静态字段)

公有/私有 类变量(静态字段)
# 公有 类变量
class Foo:
    country = "中国"

    def __init__(self):
        pass

    def func(self):
        # 内部调用
        # print(self.country) 内部 对象来调用
        print(Foo.country)  # 内部 类来调用 (推荐)


# 外部调用
obj = Foo()
print(Foo.country)  # 外部 类 来调用 (推荐)
# print(obj.country)  外部对象来调用
obj.func()


# 私有 类变量
class Foo:
    __country = "中国"

    def __init__(self):
        pass

    def func(self):
        # 内部调用
        print(self.__country)
        print(Foo.__country)  # 推荐

    @classmethod  # 类方法
    def func(cls):
        print(cls.__country)

    @staticmethod  # 静态方法
    def func():
        print(Foo.__country)  # 这两种来访问类的私有变量,外部都可以直接用类名.函数名来访问

    @property
    def func(self):
        return Foo.__country


# 外部无法调用私有类变量
obj = Foo()
# print(Foo.country)报错,无法直接调用 私有 类变量
obj.func()  # 用对象来调用 实例方法
Foo.func(obj)  # 自己传对象obj

Foo.func()

print(obj.func)  # 当做属性来访问
公有/私有 实例变量(字段/普通字段)
# 公有实例变量(字段)
class Foo:
    def __init__(self, name):
        self.name = name
        self.age = 123

    def func(self):
        print(self.name)  # 内部调用


obj = Foo('周杰伦')
print(obj.name)  # 外部调用
print(obj.age)  # 外部调用
obj.func()


# 私有实例变量
class Foo:
    def __init__(self, name):
        # 私有实例变量(私有字段)
        self.__name = name
        self.age = 123

    def func(self):
        print(self.__name)  # 内部可调用


obj = Foo('林俊杰')
print(obj.age)
# obj.__name # 无法访问
obj.func()  # 找一个内部人:func, 让func帮助你执行内部私有 __name


# 深入理解 私用实例变量
class Student(object):
    def __init__(self, name, age, height, weigth, money):
        self.name = name
        # 在python语言中,在一个属性前加上 __(两个下划线),表示该属性是私有的属性,私有的属性在类的外部不能直接访问
        # 如果外部想操作私有变量, 可以提供公开的get/set方法来获取/设置该属性
        # 使数据安全
        self.__age = age
        self.height = height
        self.weigth = weigth
        self.money = money

    # 获取__age属性值
    def getAge(self):
        return self.__age

    # 设置__age值
    def setAge(self, age):
        # 处理数据
        if age > 16 and age < 24:  # 这样可以 设置变量条件
            self.__age = age
        else:
            print("您不符合入学年龄要求")  # get 和 set 其实就内部访问变量


def eat(self):
    print("%s在吃饭" % self.name)


# 创建一个对象
student1 = Student("张家辉", 18, 180, 120, 100000)
# 修改年龄
print(student1.age)
student1.age = -19  # 报错,已经无法访问私有变量
print(student1.__age)  # 注意: 在类的外部不能直接访问私有属性

# 通过get方法访问私有变量
print(student1.getAge())

# 通过set方法来设置私有变量
student1.setAge(19)
student1.setAge(-19)  # 还可以在在setAge函数里设置条件
print(student1.getAge())

# 在python中,一个私有变量如 __A 在python解释器解释的时候,其实就是将 __A的名字改成了
# _类名__A 的形式,而我们可以通过 _类名__A的形式去访问/设置该变量(不建议这么做)

# 注意:
print(student1._Student__age)
student1._Student__age = -19
print(student1.getAge())
方法
# 没必要写实例方法
class Foo(object):

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

     def func(self):
          print('123') # 这里没有用到初始化,或者对象功能

obj = Foo('雷军')
obj.func()

# 有必要写实例方法
"""
class Foo(object):
    def __init__(self, name):
        self.name = name

    def func(self):
        print(self.name)

obj = Foo('雷军')
obj.func()
"""
静态方法
对于没有必要写实例化方法,可采取静态方法
class Foo(object):
    def __init__(self, name):
        self.name = name

    # 实例方法
    def func(self):
        print(self.name)

    # 静态方法,如果方法无需使用对象中封装的值,那么就可以使用静态方法
    @staticmethod
    def display():  # 参数self可以省略
        print(666)

    def display(a1, a2):  # 也可以传参
        return a1 + a2  # 返回值


obj = Foo('周杰伦')
obj.func()
# obj.dispaly() 可以,但不推荐
Foo.dispaly()  # 静态方法,可以直接用类调用

ret = Foo.display(1, 3)
print(ret)


# 静态方法 总结
     1. 编写时:
          方法上方写 @staticmethod
          方法参数可有可无
     2. 调用时:.方法名() (推荐)
          对象.方法名()
     3. 什么时写静态方法?
          无需调用对象中已封装的值
类方法
class Foo(object):
    def __init__(self, name):
        self.name = name

    # 实例方法,self是对象
    def func(self):
        print(self.name)

    # 静态方法,如果方法无需使用对象中封装的值,那么就可以使用静态方法
    @staticmethod
    def display(a1, a2):
        return a1 + a2

    # 类方法,cls是类
    @classmethod
    def show(cls, x1, x2):
        print(cls, x1, x2)

    # inner = cls("min") cls能获取到当前类
    # inner.func()  结果:min


# 执行类方法
Foo.show(1, 8)  # 结果:<class '__main__.Foo'> 1 8

# 类方法 总结
     1. 定义时:
          方法上方写: @classmethod
          方法的参数: 至少有一个cls参数
     2. 执行时:
          类名.方法名()  #默认会将当前类传到参数中.
     3. 什么时用?
          如果在方法中会使用到当前类,那么就可以使用类方法.
私有实例化方法 和 私有静态方法
# 私有实例化方法
class Foo(object):
     def __init__(self):
          pass

     def __display(self,arg):
          print('私有方法',arg)

     def func(self):
          self.__display(123) # 内部定义一个访问实例方法

obj = Foo()
# obj.__display(123) # 无法访问
obj.func() # 通过内部方法来访问



# 私有的静态方法
class Foo(object):

    def __init__(self):
        pass

    @staticmethod
    def __display(arg):
        print('私有静态 方法', arg)

    def func(self):  # 实例化方法 在外部通过实例化对象来调用
        Foo.__display(123)  # 本来可以用self.__display(123),但建议用类

    @staticmethod
    def get_display():  # 通过在内部再创建一个静态方法以供调用
        Foo.__display(888)


# Foo.__display(123) 报错

obj = Foo()
obj.func()
Foo.get_display()
# 两个结果一样,都可以实现
属性
Python中的属性其实是普通方法的变种。@property 将方法变成属性

属性有两种定义方式
	
多用于:会改变的量,要用于计算得到
属性的基本使用
# 属性的定义和使用
# ############### 定义 ###############
class Foo:

    def func(self):
        pass

    # 定义属性
    @property
    def prop(self):
        pass
# ############### 调用 ###############
foo_obj = Foo()

foo_obj.func()
foo_obj.prop   #调用属性


# 由属性的定义和调用要注意一下几点:

# 定义时,在普通方法的基础上添加 @property 装饰器;
# 定义时,属性仅有一个self参数
# 调用时,无需括号
	# 调用方法:foo_obj.func()
	# 调用属性:foo_obj.prop

# 注意:属性存在意义是 访问属性时可以制造出和访问字段完全相同的假象
# 属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。

>>>完成一个分页练习

class Page(object):
    def __init__(self, li, pagesize):
        self.li = li
        self.pagesize = pagesize

    def start(self):  # 第一页
        return self.li[:self.pagesize]

    def end(self):  # 最后一页
        return self.li[(self.total_num - 1) * self.pagesize:self.total_num * self.pagesize]

    def index(self):  # 跳转页
        page_num = int(input("please enter page num:"))
        if page_num < 1 and page_num > self.total_num:
            print("you enter page num is wrong!")
        return self.li[(page_num - 1) * self.pagesize:page_num * self.pagesize]

    @property
    def total_num(self):  # 计算总页数
        if len(self.li) % self.pagesize == 0:
            return len(self.li) // self.pagesize
        elif len(self.li) % self.pagesize != 0:
            return len(self.li) // self.pagesize + 1


obj = Page([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3)
print(obj.start())
print(obj.end())
print(obj.index())
属性的两种定义方式
属性的定义有两种方式:

装饰器 即:在方法上应用装饰器
静态字段 即:在类中定义值为property对象的静态字段

装饰器方式:在类的普通方法上应用@property装饰器

# Python中的类有经典类和新式类,新式类的属性比经典类的属性丰富。
#( 如果类继object,那么该类是新式类 )
# 经典类,具有一种@property装饰器(如上一步实例)

# ############### 定义 ###############    
class Goods:

    @property
    def price(self):
        return "wupeiqi"
# ############### 调用 ###############
obj = Goods()
result = obj.price  # 自动执行 @property 修饰的 price 方法,并获取方法的返回值

# 新式类,具有三种@property装饰器
# ############### 定义 ###############
class Goods(object):

    @property
    def price(self):
        print '@property'

    @price.setter
    def price(self, value):
        print '@price.setter'

    @price.deleter
    def price(self):
        print '@price.deleter'

# ############### 调用 ###############
obj = Goods()

obj.price          # 自动执行 @property 修饰的 price 方法,并获取方法的返回值

obj.price = 123    # 自动执行 @price.setter 修饰的 price 方法,并将  123 赋值给方法的参数

del obj.price      # 自动执行 @price.deleter 修饰的 price 方法

'''
经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
新式类中的属性有三种访问方式,并分别对应了
三个被@property、@方法名.setter、@方法名.deleter修饰的方法
'''
由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,
分别将三个方法定义为对同一个属性:获取、修改、删除
class Goods(object):

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deltter
    def price(self, value):
        del self.original_price

obj = Goods()
obj.price         # 获取商品价格
obj.price = 200   # 修改商品原价
del obj.price     # 删除商品原价

静态字段方式,创建值为property对象的静态字段

# 当使用静态字段的方式创建属性时,经典类和新式类无区别
class Foo:

    def get_bar(self):
        return 'wupeiqi'

    BAR = property(get_bar)

obj = Foo()
reuslt = obj.BAR        # 自动调用get_bar方法,并获取方法的返回值
print reuslt
property的构造方法中有个四个参数
第一个参数是方法名,调用 对象.属性 时自动触发执行方法
第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
第四个参数是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息
class Foodef get_bar(self):
        return 'wupeiqi'

    # *必须两个参数
    def set_bar(self, value): 
        return return 'set value' + value

    def del_bar(self):
        return 'wupeiqi'

    BAR = property(get_bar, set_bar, del_bar, 'description...')

obj = Foo()

obj.BAR              # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = "alex"     # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入
del Foo.BAR          # 自动调用第三个参数中定义的方法:del_bar方法
obj.BAE.__doc__      # 自动获取第四个参数中设置的值:description...
由于静态字段方式创建属性具有三种访问方式,我们可以根据他们几个属性的访问特点,
分别将三个方法定义为对同一个属性:获取、修改、删除
class Goods(object):

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    def get_price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    def set_price(self, value):
        self.original_price = value

    def del_price(self, value):
        del self.original_price

    PRICE = property(get_price, set_price, del_price, '价格属性描述...')

obj = Goods()
obj.PRICE         # 获取商品价格
obj.PRICE = 200   # 修改商品原价
del obj.PRICE     # 删除商品原价

注意:Python WEB框架 Django 的视图中 request.POST
就是使用的静态字段的方式创建的属性

# Django源码
class WSGIRequest(http.HttpRequest):
    def __init__(self, environ):
        script_name = get_script_name(environ)
        path_info = get_path_info(environ)
        if not path_info:
            # Sometimes PATH_INFO exists, but is empty (e.g. accessing
            # the SCRIPT_NAME URL without a trailing slash). We really need to
            # operate as if they'd requested '/'. Not amazingly nice to force
            # the path like this, but should be harmless.
            path_info = '/'
        self.environ = environ
        self.path_info = path_info
        self.path = '%s/%s' % (script_name.rstrip('/'), path_info.lstrip('/'))
        self.META = environ
        self.META['PATH_INFO'] = path_info
        self.META['SCRIPT_NAME'] = script_name
        self.method = environ['REQUEST_METHOD'].upper()
        _, content_params = cgi.parse_header(environ.get('CONTENT_TYPE', ''))
        if 'charset' in content_params:
            try:
                codecs.lookup(content_params['charset'])
            except LookupError:
                pass
            else:
                self.encoding = content_params['charset']
        self._post_parse_error = False
        try:
            content_length = int(environ.get('CONTENT_LENGTH'))
        except (ValueError, TypeError):
            content_length = 0
        self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
        self._read_started = False
        self.resolver_match = None

    def _get_scheme(self):
        return self.environ.get('wsgi.url_scheme')

    def _get_request(self):
        warnings.warn('`request.REQUEST` is deprecated, use `request.GET` or '
                      '`request.POST` instead.', RemovedInDjango19Warning, 2)
        if not hasattr(self, '_request'):
            self._request = datastructures.MergeDict(self.POST, self.GET)
        return self._request

    @cached_property
    def GET(self):
        # The WSGI spec says 'QUERY_STRING' may be absent.
        raw_query_string = get_bytes_from_wsgi(self.environ, 'QUERY_STRING', '')
        return http.QueryDict(raw_query_string, encoding=self._encoding)
    
    # ############### 看这里看这里  ###############
    def _get_post(self):
        if not hasattr(self, '_post'):
            self._load_post_and_files()
        return self._post

    # ############### 看这里看这里  ###############
    def _set_post(self, post):
        self._post = post

    @cached_property
    def COOKIES(self):
        raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '')
        return http.parse_cookie(raw_cookie)

    def _get_files(self):
        if not hasattr(self, '_files'):
            self._load_post_and_files()
        return self._files

    # ############### 看这里看这里  ###############
    POST = property(_get_post, _set_post)
    
    FILES = property(_get_files)
    REQUEST = property(_get_request)
所以,定义属性共有两种方式,分别是【装饰器】和【静态字段】,
而【装饰器】方式针对经典类和新式类又有所不同。
动态添加字段和方法、直接修改获取属性

slots 是特殊的属性,是类自己维护的, 用来限制可以使用的属性

class Student(object):
    # __slots__ 是特殊的属性,是类自己维护的, 用来限制可以使用的属性
    # __slots__ 的值是元组的形式, 每个元素是当前类可以使用的属性名,除此以外的不能使用
    # 单词“slots”:“插槽”
    __slots__ = ("age", "name", "hello", "yyy", "money", "eat")

    # 参数里为啥要加上eat,因为它虽然是方法,却也被当成了属性

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


student1 = Student("周杰伦", 35)

# python是动态语言,可以动态的给对象添加属性, 使程序更加的灵活
# 动态添加的属性,只针对当前对象有效, 其他对象不能使用

# 动态添加的属性
student1.money = 10000  # 限制随意的添加属性,设置“__slots__”中有的属性,才能添加
print(student1.name)
print(student1.money)

student2 = Student("林俊杰", 32)
# student2.money=4000
print(student2.money) # 没有自己对象上一步主动添加字段,代码会报错;不能使用studen1添加的字段


# 想在类的外部添加一个方法
# 动态的添加行为(方法)
# 错误示范:
def eat(self): # 已经是在类的外部了
     print("%s在吃饭"%(self.name))
# student2.eat() 错误,类的内部没有定义eat()

# 第一种方式:
def eat(self): #已经是在类的外部了,这参数self可以写成任意
     print("%s在吃饭"%(self.name))
     print("%s在吃饭"%(self.eat)) #<function eat at 0x00000201E2903EA0>在吃饭
     # 这已经不能算方法了,就是一个外部函数


student2.eat = eat # 这句必须写在外置函数后,因为函数名相当于声明了变量
# 相当于在 构造函数里 增添了一句:self.eat=eat 
# 测试:<function eat at 0x00000268598A3EA0>在吃饭,所以是把函数名eat封装到对象中了

# 调用
student2.eat(student2) # 调用外部eat函数,将对象传入,对象中已经封装好了name属性
# 打印结果:林俊杰在吃饭


# 第二种方式:
from types import MethodType


def eat(self):
    print("%s在吃饭" % (self.name))


# 动态的添加行为
student2.eat = MethodType(eat, student2) # 将eat函数添加到对象种
student2.eat() 
class Student(object):
    def __init__(self, name, age):
        self.name = name
        self.__age = age
	
	# 第一种获取私有字段的方式:
    # def setAge(self,age):
    # 	self.__age = age
    # def getAge(self):
    # 	return self.__age

    # 方法名是将私有变量名 去掉2个下划线 后的名字命名的
    # 并且在方法名上 加上 @property 表示私有变量,可以用:对象名.去掉下划线的名字 去访问变量
    @property  # 原单词的意思:所有物,写了这个表示把age当做属性来用了
    def age(self):
        return self.__age

    # 方法名是将私有变量名 去掉2个下划线 后的名字命名的
    # 并且在方法名上  @去掉2个下滑线后的名字.setter
    # 表示私有变量 可以以:对象名.去掉下划线的名字 = 值  的方式去修改值
    @age.setter
    def age(self, age):
        self.__age = age


student1 = Student("胖胖", 35)

# 私有属性是不能直接以  对象名.属性名 的方式调用的
# print(student1.__age) 错误

# 希望以 对象名.属性名 的方式去访问私有属性
print(student1.age)  # 35
# 如果没有加@property 的话,要这样访问:print(student1.age())#

student1.age = 100  # 修改内容
print(student1.age)

主动调用(执行)其他类的成员
手动添加参数
class Base(object):
    def f1(self):
        print('5个功能')


class Foo(object):
    # class Foo(Base):
    def f1(self):
        print('3个功能')
        Base.f1(self)  # 主动其他类的方法,因为其他类的方法需要一个参数,所以要手动的添加self
        # 这个self 是 还是当前的对象


obj = Foo()
obj.f1()

# Base.实例方法(自己传self),类名.实例方法;与继承没有关系
# 类的方法可以主动去执行其他类里非私有的方法,但是参数需要自己传
super

按照类的继承顺序(执行继承体系的下一个),按顺序找下一个类,去执行它的内容,
找不到就会报错

# super
'''
class Base(object):
    def f1(self):
        print('5个功能')


class Foo(Base):
    def f1(self):
        super().f1()
        print('3个功能')


obj = Foo()
obj.f1()
'''

class Foo(object):
    def f1(self):
        super().f1()
        print('3个功能')


class Bar(object):
    def f1(self):
        print('6个功能')


class Info(Foo, Bar):
    pass


# obj = Foo()
# obj.f1() 结果:报错,因为Foo没有继承的类,找不到

obj = Info()
obj.f1()  # 6个功能 3个功能 过程:去执行下一个继承类的f1()
super().实例方法 

super() 函数是用于调用父类(超类)的一个方法。

super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,

会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。

MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。


#单继承 中的 super()
class A:
     def __init__(self):
          self.n = 2 

     def add(self, m):
          print('self is {0} @A.add'.format(self))
          self.n += m #此时的self是B的对象,所以 self.n=3+2


class B(A):
     def __init__(self):
          self.n = 3 #执行后,B的对象改成了5

     def add(self, m):
          print('self is {0} @B.add'.format(self))
          super().add(m)
          self.n += 3  #b.n=b.n+3 

b = B()
b.add(2)
print(b.n)
'''
输出结果:
self is <__main__.B object at 0x000002198A7B2320> @B.add
self is <__main__.B object at 0x000002198A7B2320> @A.add
8

1、super().add(m) 确实调用了父类 A 的 add 方法。
2、super().add(m) 调用父类方法 def add(self, m) 时, 此时父类中 self 并不是父类的实例而是子类的实例, 所以 b.add(2) 之后的结果是 5 而不是 4 。
'''

#多继承中的super()
class A:
     def __init__(self):
          self.n = 2

     def add(self, m):
          print('self is {0} @A.add'.format(self))
          self.n += m


class B(A):
     def __init__(self):
          self.n = 3

     def add(self, m):
          print('self is {0} @B.add'.format(self))
          super().add(m)
          self.n += 3

class C(A):
     def __init__(self):
          self.n = 4

     def add(self, m):
          print('self is {0} @C.add'.format(self))
          super().add(m)
          self.n += 4


class D(B, C):
     def __init__(self):
          self.n = 5

     def add(self, m):
          print('self is {0} @D.add'.format(self))
          super().add(m)
          self.n += 5


d = D()
d.add(2)
print(d.n)

'''
输出结果:
self is <__main__.D object at 0x10ce10e48> @D.add
self is <__main__.D object at 0x10ce10e48> @B.add
self is <__main__.D object at 0x10ce10e48> @C.add
self is <__main__.D object at 0x10ce10e48> @A.add
19
'''

在这里插入图片描述

class A():
    def __init__(self):
        print('enter A')  # 4
        print('leave A')  # 5


class B(A):
    def __init__(self):
        print('enter B')  # 2
        super().__init__()
        print('leave B')  # 7


class C(A):
    def __init__(self):
        print('enter C')  # 3
        super().__init__()
        print('leave C')  # 6


class D(B, C):
    def __init__(self):
        print('enter D')  # 1
        super().__init__()
        print('leave D')  # 8


d = D()
"""
     ---> B ---
A --|          |--> D
     ---> C ---


结果:
enter D
enter B
enter C
enter A
leave A
leave C
leave B
leave D
"""
类的特殊成员
__doc__的用法
# 作用:输出描述类的信息
class Foo:
    """ 这是我写的信息,看我 """

    def func(self):
        pass


print(Foo.__doc__)
# 结果:这是我写的信息,看我
__module__和__class__的用法
__module__	表示当前操作的对象在那个模块
__class__ 	表示当前操作的对象的类是什么
lib/aa.py 文件中

# !/usr/bin/env python
# -*- coding:utf-8 -*-

class C:

    def __init__(self):
        self.name = 'jaychou'

---
from lib.aa import C

obj = C()
print obj.__module__  # 输出 lib.aa,即:输出模块
print obj.__class__      # 输出 lib.aa.C,即:输出类
__hash__和__eq__的用法
类和对象默认都是可哈希(hash)的;但是可以给对象设置成不可hash,类不能更改
class Foo(object):

    对象.__hash__= None # 此时对象就变不可哈希了
# 类和对象默认都是可哈希(hash)的,即是不可变的,也就可以作为字典的键
class Foo():
    def eat(self, food):
        print("我爱吃鱼和:", food)


class Bar():
    def eat(self, food):
        print("我爱吃肉和:", food)


dic = {Foo: "鸡蛋", Bar: "包子"} # 字典的键

for k, v in dic.items():
    k().eat(v)
# 类名:就是一个变量名
'''
输出结果:
	我爱吃鱼和: 鸡蛋
	我爱吃肉和: 包子
'''

__hash__触发时机

# __hash__ 的触发的场景有两种:

#(1)被内置函数hash()调用

#(2)hash类型的集合对自身成员的hash操作:
# -- set(), frozenset([iterable]), dict(**kwargs)

class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __hash__(self):
        return hash(str(self.a)+str(self.b))
a = A()
print(hash(a))

__ eq__的触发时机

# == 时触发
class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __eq__(self,obj):
        if  self.a == obj.a and self.b == obj.b:
            return True
a = A()
b = A()
print(a == b)
------
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # def __hash__(self):
    #     print(self.name, '使用了__hash__方法')
    #     return hash(self.name)

    def __eq__(self, other):
        print(self.name, '使用了__eq__方法')
        return self.__dict__ == other.__dict__ 
        # __dict__取出对象封装的所有值,再使用bool判断,返回True
        # 和上例子中↓ 是一样的:
       	'''
       	if  self.a == obj.a and self.b == obj.b:
           	return True 
        '''

person1 = Person('周杰伦', 20)
person2 = Person('林俊杰', 20)
person3 = Person('陈奕迅', 30)
person4 = Person('周杰伦', 20)

print(person1 == person4)
print(person2 == person3)
'''
输出结果:
周杰伦 使用了__eq__方法
True
林俊杰 使用了__eq__方法
False

注:以上代码只用到了__eq__,因为没涉及触发__hash__三种;放开注释代码也不会执行
'''

如果代码重写了__eq__没有重写__hash__又使用了
set(), frozenset([iterable]), dict(**kwargs) 哈希集合的元素,那代码会报错

class Person:

    def __init__(self, name, age):

        self.name = name
        self.age = age

    # def __hash__(self):
    #     print(self.name, '使用了__hash__方法')
    #     return hash(self.name)

    def __eq__(self, other):
        print(self.name, '使用了__eq__方法')
        return self.__dict__ == other.__dict__


person1 = Person('周杰伦', 20)
person2 = Person('林俊杰', 20)
person3 = Person('陈奕迅', 30)
person4 = Person('周杰伦', 20)

# print(person1 == person4)
# print(person2 == person3)

set1 = {person1, person2, person3, person4} # 使用了
print(set1)
# 结果报错:TypeError: unhashable type: 'Person'

# 这其实是因为重写__eq__()方法后会默认把__hash__赋为None;如下测试代码:
class A:
    def __eq__(self, other):
        pass


a = A()

print(a.__hash__)  # 设置为了None,变为不可哈希

hash(a) # 此时打印报错:TypeError: unhashable type: 'A'

如果自定义类重写了__hash__()方法没有重写__eq__()方法

class Person:

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

    def __hash__(self):
        print(self.name, '使用了__hash__方法')
        return hash(self.name)

    # def __eq__(self, other):
    #     print(self.name, '使用了__eq__方法')
    #     return self.__dict__ == other.__dict__


person1 = Person('周杰伦', 20)
person2 = Person('林俊杰', 20)
person3 = Person('陈奕迅', 30)
person4 = Person('周杰伦', 20)

set1 = {person1, person2, person3, person4}  # 使用了
print(set1)
'''
输出结果:
周杰伦 使用了__hash__方法
林俊杰 使用了__hash__方法
陈奕迅 使用了__hash__方法
周杰伦 使用了__hash__方法
{<__main__.Person object at 0x0000022DB1C29A20>, 
 <__main__.Person object at 0x0000022DB1C29A58>,
 <__main__.Person object at 0x0000022DB1C29A90>,
 <__main__.Person object at 0x0000022DB1C29AC8>}
注意:person1与person4是同一个人,内存地址不一样,却被添加到了不同的集合


原因:主要是因为当发现hash出的值相同时,就需要__eq__进行下一步判断。
	重写了__hash__却没重写__eq__,于是默认去调用了object的__eq__,
	而默认的__eq__比较的是对象地址,person1与persperson4地址不同,
	所以将最后的person4加到了新集合。


# 如果上述 __hash__ 和__eq__都重写了
# 输出结果只有三个,相同的共有一个内存地址
{<__main__.Person object at 0x0000026215659A90>,
 <__main__.Person object at 0x0000026215659A58>,
 <__main__.Person object at 0x0000026215659A20>}

'''
class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __hash__(self):
        return hash(self.name+self.sex)

    def __eq__(self, other):
        if self.name == other.name and self.sex == other.sex:
            return True


p_lst = []
for i in range(84):
    p_lst.append(Person('jaychou',i,'male'))

print(p_lst) # 打印所有对象内存地址
print(set(p_lst)) #{<__main__.Person object at 0x000001261E1699E8>}
# 去重后打印的结果和第一个元素内存地址一样
__new__和__metaclass__的用法
class Foo(object):
 
    def __init__(self):
        pass
 
obj = Foo()   # obj是通过Foo类实例化的对象

# 上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,
# --Foo类本身也是一个对象,因为在Python中一切事物都是对象。

# 如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,
# --那么Foo类对象应该也是通过执行某个类的 构造方法 创建。

print type(obj) # 输出:<class '__main__.Foo'>     表示,obj 对象由Foo类创建
print type(Foo) # 输出:<type 'type'>              表示,Foo类对象由 type 类创建
所以,obj对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,
即:Foo类对象 是通过type类的构造方法创建,
那么,创建类就可以有两种方式:
# 普通方式
class Foo(object):
 
    def func(self):
        print('hello world')

# 特殊方式(type类的构造函数)
def func(self):
    print('hello world')
 
Foo = type('Foo',(object,), {'func': func})
# type第一个参数:类名
# type第二个参数:当前类的基类
# type第三个参数:类的成员
# 类 是由 type 类实例化产生

# 那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?
# --类又是如何创建对象?

# 答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,
# --所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。

在这里插入图片描述

class MyType(type):

    def __init__(self, what, bases=None, dict=None):
        super(MyType, self).__init__(what, bases, dict)

    def __call__(self, *args, **kwargs):
        obj = self.__new__(self, *args, **kwargs)

        self.__init__(obj)

class Foo(object):

    __metaclass__ = MyType

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

    def __new__(cls, *args, **kwargs):
        return object.__new__(cls, *args, **kwargs)

# 第一阶段:解释器从上到下执行代码创建Foo类
# 第二阶段:通过Foo类创建obj对象
obj = Foo()
类和对象操作时触发的特殊方法
class Foo(object):

    def __init__(self, a1, a2):
        self.a1 = a1
        self.a2 = a2

    def __call__(self, *args, **kwargs):
        print(11111, args, kwargs)
        return 123

    def __getitem__(self, item):
        print(item)
        return 8

    def __setitem__(self, key, value):
        print(key, value, 111111111)

    def __delitem__(self, key):
        print(key)

    def __add__(self, other):
        return self.a1 + other.a2

    def __enter__(self):
        print('1111')
        return 999

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('22222')
# 类名() 自动执行 __init__
obj = Foo(1,2)

# 对象() 或者 类()() 自动执行 __call__

ret = obj(6,4,2,k1=456)
print(callable(ret)) # True

# 对象['xx']  自动执行 __getitem__
ret = obj['yu']
print(ret)

# 对象['xx'] = 11  自动执行 __setitem__
obj['k1'] = 123

# del 对象[xx]     自动执行 __delitem__
del obj['uuu']


>>>例子:打印出 123
class Foo(object):
    def __init__(self):
        self.dic = {}

    def __setitem__(self, key, value):
        self.dic[key] = value

    def __getitem__(self, item):
        return self.dic.get(item)


obj = Foo()  # 执行__init__  拿到一个空字典
obj["x"] = 123  # 执行__setitem__  dic[x]=123
print(obj["x"])  # 执行__getitem__ 返回 dic(x)的值,即是123

# 对象+对象 自动执行 __add__
obj1 = Foo(1,2)
obj2 = Foo(88,99)
ret = obj2 + obj1
print(ret)

# with 对象 自动执行 __enter__ / __exit__
obj = Foo(1,2)
with obj as f:
     print(f)
     print('内部代码')
__add__和__radd__的用法

在这里插入图片描述

__len__的用法
>>>判断下一个类中的成员个数
class Foo(object):
    def __init__(self):
        pass

    var1 = 1
    var2 = 2
    __var3 = 3

    def func1(self):
        pass

    def func2(self):
        pass

    def __func3(self):
        pass

    def __len__(self):
        # lst = []
        lst = [k for k in Foo.__dict__ if not (k.startswith('__') and k.endswith('__'))]
        return len(lst)


obj = Foo()
ret = len(obj)
print(ret)

在这里插入图片描述

__getitem__和__setitem__和__delitem__的用法
dic = {'k': 'v'}
# 对象 : 存储属性 和调用方法
dic['k'] = 'v'
class Foo:
    def __init__(self, name):
        self.name = name

    def __getitem__(self, item):
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __delitem__(self, key):
        print('del obj[key]时,我执行')
        self.__dict__.pop(key)

    def __delattr__(self, item):
        print('del obj.key时,我执行')
        self.__dict__.pop(item)


f1 = Foo('jaychou')
f1['age'] = 18
f1['age1'] = 19
del f1.age1
del f1['age']
f1['name'] = 'eason'
print(f1.__dict__)

'''
del obj.key时,我执行
del obj[key]时,我执行
{'name': 'eason'}
'''

纸牌游戏练习:用到 __len__和__item__系列

from collections import namedtuple # 引入命名元组
# rank 牌面的大小 suit牌面的花色(这个相当于一个只有属性的类)

Card = namedtuple('Card', ['rank', 'suit'])


class FranchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = ['红心', '方板', '梅花', '黑桃']

    def __init__(self):
        self._cards = [Card(rank, suit) for rank in FranchDeck.ranks
                       for suit in FranchDeck.suits]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, item):
        return self._cards[item]


deck = FranchDeck()
print(deck[0])
from random import choice

print(choice(deck))
print(choice(deck))

'''
Card(rank='2', suit='红心')
Card(rank='K', suit='红心')
Card(rank='J', suit='黑桃')
'''
# 纸牌游戏 二:

from collections import namedtuple

Card = namedtuple('Card', ['rank', 'suit'])


class FranchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = ['红心', '方板', '梅花', '黑桃']

    def __init__(self):
        self._cards = [Card(rank, suit) for rank in FranchDeck.ranks
                       for suit in FranchDeck.suits]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, item):
        return self._cards[item]

    def __setitem__(self, key, value):
        self._cards[key] = value


deck = FranchDeck()
print(deck[0])
from random import choice

print(choice(deck))
print(choice(deck))

from random import shuffle

shuffle(deck)
print(deck[:5])

'''
Card(rank='2', suit='红心')
Card(rank='6', suit='黑桃')
Card(rank='K', suit='黑桃')

[Card(rank='7', suit='红心'), 
Card(rank='K', suit='黑桃'), 
Card(rank='10', suit='梅花'), 
Card(rank='2', suit='梅花'), 
Card(rank='A', suit='方板')]
'''
__setattr__和__getattr__的用法

对象.属性=值 执行__setattr__
res=对象.属性 执行__getattr__
print(res)

在这里插入图片描述

上图中,构造方法self.name不写,在后面print中obj.name调用__getattr__同样可以输出名字
如果不写,会默认继承object类中的init方法,所以同样可以完成
class Foo(object):
    def __init__(self):
        self.name = "min"  # 默认自己类中执行__setattr__

    def __setattr__(self, key, value):  # 上面进来执行,结果发现,↓↓↓
        object.__setattr__(self, key, value)  # 主动执行父类中的方法
        # 类.函数()需要自己传参数,给对象中设置 name="min"


obj = Foo()
print(obj.name)
obj.xx = 123

在这里插入图片描述

这句话:object.__seatter__(self,"info",{}) 
实质上就是 在__inint__中写一句:self.info={}
因为你执行__setattr__就等于:对象.属性=value

在这里插入图片描述

上面 对象中已经设置到了一个字典,后面再给字典中增添一组值

在这里插入图片描述

item 也是传入的 key,也就是 name;所以可以通过字典取到值
这就是 初始化设置值得原理
class Foo(object):
    def __init__(self):
        object.__setattr__(self, "info", {})

    def __setattr__(self, key, value):
        self.info[key] = value

    def __getattr__(self, item):
        return self.info[item]


obj = Foo()

obj.xx = 123
obj.name = "min"
print(obj.xx)
print(obj.name)  # 结果:123 min
真正的构造方法
class Foo(object):
    def __init__(self, a1, a2):  # 它只是初始化方法
        """
        为空对象进行数据初始化
        :param a1:
        :param a2:
        """
        self.a1 = a1
        self.a2 = a2

    def __new__(cls, *args, **kwargs):  # 构造方法,创建时,它可以开辟内存
        """
        创建一个空对象     
        :param args:
        :param kwargs:
        :return:
        """
        return object.__new__(cls)  # Python内部创建一个当前类的对象(初创时内部是空的)


obj1 = Foo(1, 2)
print(obj1)

obj2 = Foo(11, 12)
print(obj2)
__str__和__repr__的用法
class Foo(object):
    def __init__(self):
        pass

    def func(self):
        pass

    def __str__(self):
        return "min"


obj = Foo()
print(obj, type(obj))  # min <class '__main__.Foo'>
# 打印对象,会自动执行__str__方法,必须要有返回值,而且只能是字符串类型

在这里插入图片描述
在这里插入图片描述

dict 的用法

对象.__dict__使用

# 取出对象封装的所有值
class Foo(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def func(self):
        pass


obj1 = Foo('格雷西', 99)
obj2 = Foo('路易', 89)

print(obj1.__dict__)  # {'name': '格雷西', 'age': 99}
print(obj2.__dict__)  # {'name': '路易', 'age': 89}
__iter__的用法
# l1是list类的一个对象,可迭代对象
l1 = [11,22,33,44]

#l2是list类的一个对象,可迭代对象
l2 = [1,22,3,44]

class Foo(object):
     def __init__(self,name,age):
          self.name = name
          self.age = age

     def func(self):
          pass

#obj1是Foo类的一个对象,不可迭代对象
obj1 = Foo('周杰伦',99)

for item in obj1:
     print(item)
     
------
class Foo(object):
     def __init__(self,name,age):
          self.name = name
          self.age = age

     def func(self):
          pass

     def __iter__(self): # 只要含有__iter__就是可迭代对象了
          return iter([11,22,33,44,55,66])

          yield 11
          yield 22
          yield 33

# obj1是Foo类的一个对象,可迭代对象
"""
如果想要把不可迭代对象 -> 可迭代对象
1. 在类中定义__iter__方法
2. iter内部返回一个迭代器(生成器也是一种特殊迭代器)
"""
obj1 = Foo('雷格西',99)
for item in obj1:
     print(item)

在这里插入图片描述
在这里插入图片描述

练习题
class StarkConfig(object):
    def __init__(self, num):
        self.num = num

    def run(self):
        self()

    def __call__(self, *args, **kwargs):
        print(self.num)


class RoleConfig(StarkConfig):
    def __call__(self, *args, **kwargs):
        print(345)

    def __getitem__(self, item):
        return self.num[item]


v1 = RoleConfig("abex")
v2 = StarkConfig("wupeiqi")
print(v1[1])  # ret=self.num[1] alex[1]  b
print(v2[2])  # 报错,没有 __getitem__
# 关于调用 类变量 和实例变量 的问题
# 第一题:
class StarkConfig(object):
    list_display = []

    def get_list_display(self):
        self.list_display.insert(0, 33)
        return self.list_display


class RoleConfig(StarkConfig):
    list_display = [11, 22]


s1 = StarkConfig()

result1 = s1.get_list_display()
print(result1)  # 33

result2 = s1.get_list_display()
print(result2)  # 33 33


# 第二题:

class StarkConfig(object):
    list_display = []

    def get_list_display(self):
        self.list_display.insert(0, 33)
        return self.list_display


class RoleConfig(StarkConfig):
    list_display = [11, 22]


s1 = StarkConfig()
s2 = StarkConfig()

result1 = s1.get_list_display()
print(result1)  # [33]

result2 = s2.get_list_display()
print(result2)  # [33, 33]


# 第三题:
class StarkConfig(object):
    list_display = []

    def get_list_display(self):
        self.list_display.insert(0, 33)
        return self.list_display


class RoleConfig(StarkConfig):
    list_display = [11, 22]


s1 = StarkConfig()
s2 = RoleConfig()

result1 = s1.get_list_display()
print(result1)  # [33]

result2 = s2.get_list_display()  # 继承后,先找自己,再找父类
print(result2)  # [33, 11, 22]


# 第四题:
class StarkConfig(object):
    list_display = []

    def get_list_display(self):
        self.list_display.insert(0, 33)
        return self.list_display


class RoleConfig(StarkConfig):
    list_display = [11, 22]


s1 = RoleConfig()
s2 = RoleConfig()

result1 = s1.get_list_display()
print(result1)  # [33, 11, 22]

result2 = s2.get_list_display()
print(result2)  # [33, 33, 11, 22]
issubclass 与 type 与 isinstance
issubclass的作用
# issubclass
# 两个参数都必须是类
class Base(object):
    pass


class Foo(Base):
    pass


class Bar(Foo):
    pass


print(issubclass(Bar, Base))  # 检查第一个类是否是第二个参数的 子子孙孙类
type的作用

告知当前的数据类型;获取当前对象是由那个类创建,精准的告知对象的数据类型

class Foo(object):
    pass


obj = Foo()

print(obj, type(obj))  # 获取当前对象是由那个类创建
if type(obj) == Foo:
    print('obj是Foo类型')

>>>练习
class Foo(object):
    pass


class Bar(object):
    pass


def func(*args):
    foo_counter = 0
    bar_counter = 0
    for item in args:
        if type(item) == Foo:
            foo_counter += 1
        elif type(item) == Bar:
            bar_counter += 1
    return foo_counter, bar_counter


# result = func(Foo(),Bar(),Foo())
# print(result)

v1, v2 = func(Foo(), Bar(), Foo())
print(v1, v2)  # (2,1)
isinstance的作用

只能向上判断,不能向下判断

class Base(object):
    pass


class Foo(Base):
    pass


obj1 = Foo()
print(isinstance(obj1, Foo))  # 检查第一个参数(对象)是否是第二个参数(类及父类)的实例。
print(isinstance(obj1, Base))  # 检查第一个参数(对象)是否是第二个参数(类及父类)的实例。

obj2 = Base()
print(isinstance(obj2, Foo))  # False
print(isinstance(obj2, Base))  # True


def jud(a, b):
    if isinstance(a, int or float) and isinstance(a, int or float):
        return a + b
'''
总结:
给你一个参数,判断对象是不是由某一个指定类? type     --> type(obj) == Foo
给你一个参数,判断对象是不是由某一个指定类或其父类? isinstance     --> instance(obj,Foo)
'''
>>>模拟一个射击游戏
class Base(object):
    def __init__(self, name, gender, life_value, weapon):
        self.name = name
        self.gender = gender
        self.life_value = life_value
        self.weapon = weapon


class Police(Base):
    def introduction(self):
        tpl = "我是%s,我是警察" % (self.name)
        print(tpl)

    def attack(self, other):
        if isinstance(other, Police):
            print("警察是自己人,不能开枪")
            return
        print("警察%s,开枪射击匪徒%s" % (self.name, other.name))
        self.life_value -= 10
        other.life_value -= 100


class Badman(Base):
    def introduction(self):
        tpl = "我是%s,我是匪徒" % (self.name)
        print(tpl)

    def attack(self, other):
        if isinstance(other, Badman):
            print("匪徒是自己人,不能开枪")
            return
        print("匪徒%s,开枪射击警察%s" % (self.name, other.name))
        self.life_value -= 10
        other.life_value -= 100


j1 = Police("周杰伦", "男", 100, "枪")
j2 = Police("梁朝伟", "男", 100, "枪")
b1 = Badman("吴彦祖", "男", 1000, "机关枪")
b2 = Badman("刘德华", "男", 1000, "机关枪")
j1.attack(j2)  # 警察是自己人,不能开枪
j1.attack(b1)  # 警察周杰伦,开枪射击匪徒吴彦祖
b1.attack(b2)  # 匪徒是自己人,不能开枪
如何判断是否是 函数 还是 方法
from types import MethodType, FunctionType


def check(arg):
    """
    检查arg是方法还是函数?
    :param arg:
    :return:
    """
    if isinstance(arg, MethodType):  # 注意是 实例对象
        print('arg是一个方法')
    elif isinstance(arg, FunctionType):
        print('arg是一个函数')
    else:
        print('不知道是什么')


def func():
    pass


class Foo(object):
    def detail(self):
        pass

    @staticmethod
    def xxx():
        pass

    @classmethod
    def ppp(cls):
        pass


check(func)  # 直接将对象 func传入,万物皆对象

obj = Foo()
check(obj.detail)  # 方法
check(Foo.detail)  # 函数
check(obj.xxx)
check(Foo.xxx)  # 对于静态方法都是 函数
check(obj.ppp)
check(Foo.ppp)  # 对于类方法都是 方法

'''
输出结果:
     arg是一个函数
     arg是一个方法
     arg是一个函数
     arg是一个函数
     arg是一个函数
     arg是一个方法
     arg是一个方法
'''
# 特点:
class Foo(object):

    def f1(self):
        pass

    def f2(self):
        pass

    def f3(self):
        pass


# obj = Foo()
# print(obj.f1)
# print(obj.f2) #这样实例调用时,里面的都是方法

obj = Foo()
Foo.f1(obj)  # 直接用类名调用函数,会把f1当做函数,不会自动传入对象,所以要自己手动传入(不推荐使用)
# 这是一种名称空间的调用方法,实质上是函数

obj = Foo()
obj.f1()  # 把f1当做方法,自动传self值
# 练习
class Foo(object):

    def f1(self):
        pass

    def f2(self):
        pass

    def f3(self):
        pass

    list_display = [f1, f2]


obj = Foo()
Foo.list_display.append(obj.f3)  # 对象.函数名 调用

for item in Foo.list_display:
    print(item)
总结:到底方法函数?
	对象.xxx  --> xxx就是方法
	类.xxx    --> xxx就是函数
	xxx       --> xxx就是函数

关系

依赖关系

执行某个动作是,需要其他类来帮助完成(用到其他对象),此时关系是最紧密的

在这里插入图片描述

# 模拟一个植物大战僵尸的场景
class Zhiwu(object):
    def __init__(self, name, hp, at):
        self.name = name
        self.hp = hp
        self.at = at

    def attack(self, js):
        print("植物开始攻击僵尸")
        js.hp -= self.at
        print(f"僵尸掉了{self.at}血,僵尸还剩下{js.hp}血")


class Janshi(object):
    def __init__(self, name, hp, at):
        self.name = name
        self.hp = hp
        self.at = at

    def attack(self, zw):
        print("僵尸开始攻击植物")
        zw.hp -= self.at
        print(f"植物掉了{self.at}血,植物还剩下{zw.hp}血")


zw = Zhiwu("豌豆射手", 10, 100)
js = Janshi("铁桶", 1000, 1)
zw.attack(js)
js.attack(zw)
zw.attack(js)
js.attack(zw)

关联关系

我需要你,而且你还要是属于我

# 一对一的关系
class Boy(object):
    def __init__(self, name, girl_friend=None):
        self.girl_friend = girl_friend  # 一对一体现在这
        # 在初始化时可以给一个对象的属性设置成另一个类的对象

    def eat(self):
        if self.girl_friend:
            print(f"带着女朋友{self.girl_friend.name}一起吃饭")
        else:
            print('自己一个人吃饭')

    def movie(self):
        if self.girl_friend:
            print(f"带着女朋友{self.girl_friend.name}一起看电影")
        else:
            print('自己一个人看电影')


class Girl(object):
    def __init__(self, name):
        self.name = name


b = Boy("敏浩")
g = Girl("美丽")
b.eat()

b.girl_friend = g
b.eat()
# 一对多的关系
class School(object):
    def __init__(self, name):
        self.teacher_li = []

    def zhaoping(self, teacher):
        self.teacher_li.append(teacher)

    def shangke(self):
        for i in self.teacher_li:
            print(f'{i.name}在上课')


class Teacher(object):
    def __init__(self, name):
        self.name = name


school = School("白马")
t1 = Teacher("min")
t2 = Teacher("like")
t3 = Teacher("yep")

school.zhaoping(t1)
school.zhaoping(t2)
school.zhaoping(t3)
school.shangke()  # 一个学校多个老师在上课

在这里插入图片描述

对象与对象间的嵌套
"""
创建三个学校且三个学校的设施内容等都是一致.
"""
class School(object):
    def __init__(self, name, address):
        self.name = name
        self.address = address

    def speech(self):
        print('讲课')


obj1 = School('北京校区', '朝阳')
obj2 = School('上海校区', '浦东新区')
obj3 = School('深圳校区', '南山区')


class Teacher(object):
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.__salary = salary
        self.school = None  # 等于给他准备一个空盒子,就是不用参数


t1 = Teacher('周杰伦', 19, 188888)
t2 = Teacher('林俊杰', 18, 60)
t3 = Teacher('陈奕迅', 16, 900000)

# 老师分配校区
t1.school = obj1
t2.school = obj1
t3.school = obj2

# 查看t1老师,所在的校区名称/地址
print(t1.school.name)
print(t1.school.address)
print(t1.name)
print(t1.age)
t1.school.speech()

面向对象的更多说明

面向对象的软件开发
	在学完了python的class机制之后,遇到一个生产中的问题,还是会懵逼,这其实太正常了,因为任何程序的开发都是先设计后编程,
python的class机制只不过是一种编程方式,如果你硬要拿着class去和你的问题死磕,变得更加懵逼都是分分钟的事,在以前,
软件的开发相对简单,从任务的分析到编写程序,再到程序的调试,可以由一个人或一个小组去完成。但是随着软件规模的迅速增大,
软件任意面临的问题十分复杂,需要考虑的因素太多,在一个软件中所产生的错误和隐藏的错误、未知的错误可能达到惊人的程度,
这也不是在设计阶段就完全解决的。

    所以软件的开发其实一整套规范,我们所学的只是其中的一小部分,一个完整的开发过程,需要明确每个阶段的任务,
在保证一个阶段正确的前提下再进行下一个阶段的工作,称之为软件工程

    面向对象的软件工程包括下面几个部:

1.面向对象分析(object oriented analysis ,OOA)

    软件工程中的系统分析阶段,要求分析员和用户结合在一起,对用户的需求做出精确的分析和明确的表述,
    从大的方面解析软件系统应该做什么,而不是怎么去做。面向对象的分析要按照面向对象的概念和方法,在对任务的分析中,
    从客观存在的事物和事物之间的关系,贵南出有关的对象(对象的‘特征’和‘技能’)以及对象之间的联系,
    并将具有相同属性和行为的对象用一个类class来标识。

    建立一个能反映这是工作情况的需求模型,此时的模型是粗略的。

2 面向对象设计(object oriented design,OOD)

    根据面向对象分析阶段形成的需求模型,对每一部分分别进行具体的设计。

    首先是类的设计,类的设计可能包含多个层次(利用继承与派生机制)。
    然后以这些类为基础提出程序设计的思路和方法,包括对算法的设计。

    在设计阶段并不牵涉任何一门具体的计算机语言,而是用一种更通用的描述工具(如伪代码或流程图)来描述

3 面向对象编程(object oriented programming,OOP)

    根据面向对象设计的结果,选择一种计算机语言把它写成程序,可以是python

4 面向对象测试(object oriented test,OOT)

    在写好程序后交给用户使用前,必须对程序进行严格的测试,测试的目的是发现程序中的错误并修正它。

    面向对的测试是用面向对象的方法进行测试,以类作为测试的基本单元。

5 面向对象维护(object oriendted soft maintenance,OOSM)

    正如对任何产品都需要进行售后服务和维护一样,软件在使用时也会出现一些问题,
    或者软件商想改进软件的性能,这就需要修改程序。

    由于使用了面向对象的方法开发程序,使用程序的维护比较容易。

    因为对象的封装性,修改一个对象对其他的对象影响很小,利用面向对象的方法维护程序,
    大大提高了软件维护的效率,可扩展性高。

 

    在面向对象方法中,最早发展的肯定是面向对象编程(OOP),那时OOA和OOD都还没有发展起来,
    因此程序设计者为了写出面向对象的程序,还必须深入到分析和设计领域,尤其是设计领域,
    那时的OOP实际上包含了现在的OOD和OOP两个阶段,这对程序设计者要求比较高,许多人感到很难掌握。

    现在设计一个大的软件,是严格按照面向对象软件工程的5个阶段进行的,这个5个阶段的工作不是由一个人从头到尾完成的,
    而是由不同的人分别完成,这样OOP阶段的任务就比较简单了。程序编写者只需要根据OOd提出的思路,用面向对象语言编写出程序既可。

    在一个大型软件开发过程中,OOP只是很小的一个部分。

    对于全栈开发的你来说,这五个阶段都有了,对于简单的问题,不必严格按照这个5个阶段进行,
    往往由程序设计者按照面向对象的方法进行程序设计,包括类的设计和程序的设计
几个概念的说明
1.面向对象的程序设计看起来高大上,所以我在编程时就应该保证通篇class,这样写出的程序一定是好的程序
(面向对象只适合那些可扩展性要求比较高的场景)

2.在基于面向对象编程时,不一定要定义的类中完整的包含这三种特性

3.类有类属性,实例有实例属性,所以我们在定义class时一定要定义出那么几个类属性,想不到怎么办,
那就使劲的想,定义的越多越牛逼;这就犯了一个严重的错误,程序越早面向对象,死的越早,为啥面向对象,
因为我们要将数据与功能结合到一起,程序整体的结构都没有出来,或者说需要考虑的问题你都没有搞清楚个八九不离十,
你就开始面向对象了,这就导致了,你在那里干想,自以为想通了,定义了一堆属性,结果后来又都用不到,
或者想不通到底应该定义啥,那就一直想吧,想着想着就疯了。
面向对象常用术语
# 抽象/实现

抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于 绘程序结构,从而实现这种模型。
抽象不仅包括这种模型的数据属性,还定义了这些数据的接口。

对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户 程序应当是透明而且无关的。 

# 封装/接口

封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。通过任何客户端直接对数据的访问,
无视接口,与封装性都是背道而驰的,除非程序员允许这些操作。作为实现的 一部分,客户端根本就不需要知道在封装之后,
数据属性是如何组织的。在Python中,所有的类属性都是公开的,但名字可能被“混淆”了,以阻止未经授权的访问,
但仅此而已,再没有其他预防措施了。这就需要在设计时,对数据提供相应的接口,
以免客户程序通过不规范的操作来存取封装的数据属性。

注意:封装绝不是等于“把不想让别人看到、以后可能修改的东西用private隐藏起来”

真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明

(注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在)


# 合成

合成扩充了对类的 述,使得多个不同的类合成为一个大的类,来解决现实问题。
合成 述了 一个异常复杂的系统,比如一个类由其它类组成,更小的组件也可能是其它的类,数据属性及行为, 
所有这些合在一起,彼此是“有一个”的关系。

# 派生/继承/继承结构

派生描述了子类衍生出新的特性,新类保留已存类类型中所有需要的数据和行为,
但允许修改或者其它的自定义操作,都不会修改原类的定义。
继承描述了子类属性从祖先类继承这样一种方式
继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系。

# 泛化/特化

基于继承
泛化表示所有子类与其父类及祖先类有一样的特点。
特化描述所有子类的自定义,也就是,什么属性让它与其祖先类不同。

# 多态与多态性

多态指的是同一种事物的多种状态:水这种事物有多种不同的状态:冰,水蒸气

多态性的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。

冰,水蒸气,都继承于水,它们都有一个同名的方法就是变成云,但是冰.变云(),
与水蒸气.变云()是截然不同的过程,虽然调用的方法都一样

# 自省/反射

自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,

这是一项强大的特性。如果Python不支持某种形式的自省功能,dirtype内建函数,将很难正常工作。
还有那些特殊属性,像__dict__,__name__及__doc__

练习题

'''
实现:下面功能
     1. while循环提示⽤户输⼊:⽤户名、密码、邮箱(正则满⾜邮箱格式)
     2. 为每个⽤户创建⼀个对象,并添加到列表中。
     3. 当列表中的添加了3个对象后,跳出循环并以此循环打印所有⽤户的姓名和邮箱。
'''

import re


class User:
    def __init__(self, name, pwd, email):
        self.name = name
        self.pwd = pwd
        self.email = email


class Account:
    def __init__(self):  # 定义一个列表
        self.user_list = []

    def register(self):
        while True:
            name = input('请输入用户名:')
            pwd = input('请设置密码:')
            email = input("请输入邮箱:")
            str_email = "[0-9a-zA-Z][\w\-.]+@[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*\.[A-Za-z0-9]{2,6}"
            if (re.search(str_email, email)).group() == email:
                usr = User(name, pwd, email)  # 将输入的符合要求的内容,封装在usr对象中
                self.user_list.append(usr)  # 将循环等到的不同对象添加到列表中
            if len(self.user_list) == 3:
                break

    def show_info(self):
        for user in self.user_list:  # 遍历列表
            print("姓名是:%s,密码是:%s,邮箱是:%s" % (user.name, user.pwd, user.email))

    def run(self):  # 启动程序
        self.register()
        self.show_info()


if __name__ == '__main__':  # 入口
    obj = Account()
    obj.run()
>>>实现用户注册和登录功能,将用户名和密码以一个对象的形式存在一个列表中

class User:
    def __init__(self, name, pwd):
        self.name = name
        self.pwd = pwd


class Account:
    def __init__(self):
        self.user_list = []

    def register(self):  # 注册
        for i in range(2):
            name = input('请输入用户名:')
            pwd = input('请设置密码:')
            usr = User(name, pwd)
            self.user_list.append(usr)

    def login(self):  # 登录,校验
        name = input('请输入账号:')
        pwd = input('请输入密码:')
        flag = False
        for user in self.user_list:
            if name == user.name and pwd == user.pwd:
                flag = True
                break
        if flag:  # 此时的flag已经变成 True 了
            print('登录成功')
        else:
            print('登录失败')

    def run(self):  # 运行
        self.register()
        self.login()


if __name__ == '__main__':
    obj = Account()
    obj.run()

>>>题目:模拟 人开枪,射击子弹

#创建一个包:person_shooting

person_shooting.py # 主程序

'''
人 开 枪 射击 子弹
类名:人Person
属性: 枪
行为: 开枪

类名: 枪
属性: 弹夹
行为: 射击

类名: 弹夹
属性: 子弹的数量
行为: 装弹, 获取弹夹剩余的子弹数量
'''
格式: from 模块名  import 函数/属性/from person1 import Person 
from gun2 import Gun
from clip3 import Clip

# 创建弹夹
clip=Clip()
clip.set_number(30)

# 创建枪
gun= Gun(clip) # 枪要弹夹

# 创建人
person=Person(gun) # 人需要枪
# 为什么这两个对象需要参数?因为开始为它们设置了构造函数默认的参数

# 开枪
for i in range(1,31):
     person.shoot()


person1.py

#人 开 枪 射击 子弹
#类名:人Person
#属性: 枪
#行为: 开枪, 换弹夹

class Person(object):
     def __init__(self,gun):# 创建时,人就带有枪,所以要用构造函数
          self.gun=gun

     def shoot(self):# 开枪,定义一个shoot方法
          self.gun.shooting()# 让具体对象的枪,调用一个shooting方法,射击


gun2.py
#类名: 枪
#属性: 弹夹
#行为: 射击

class Gun(object):
     def __init__(self,clip): # 创建自带弹夹
          self.clip=clip

     def shooting(self): # 枪的射击方法
          # 从弹夹取出子弹
          # 弹夹可能是空的
          # 弹夹可能只有1颗子弹
          number=self.clip.get_number() # 使用私有变量获取到子弹的数量,然后赋值给number是方便
               #print(number)
          if number<=0:
               print("您已经没有子弹了,请更换弹夹")
          elif number==1:
               self.clip.set_number(number-1)
               print("子弹打完了,请更换弹夹")
          elif number<=30:
               self.clip.set_number(number-1)
               print("您打了1发子弹,剩余%d" % (self.clip.get_number()))


clip3.py
# 类名: 弹夹
# 属性: 子弹的数量
# 行为: 装弹, 获取弹夹剩余的子弹数量

class Clip(object):
     def __init__(self):
          self.__number=0  # 将子弹设为私有变量

     def get_number(self):
          return self.__number

     def set_number(self,num):
          if num>=1  and num <= 30:
               self.__number=num
          else:
               print("你已经没有子弹了,请装弹")

# 在哪个设置了“私有变量”,该类里,一定要有 get 和 set 函数
# get一般不需要参数,return self.私有变量 就行
# set 可以设置参数,一般是来设置私有变量后将值赋给私有变量
# 类的成员练习
class Foo(object):
    a1 = 11
    a2 = 22

    def __init__(self):
        self.a1 = 1


obj = Foo()
print(obj.a1)
print(obj.a2)  # 输出结果:1,22


# 2
class Foo(object):
    a1 = 11

    def __init__(self, num):
        self.a2 = num


obj = Foo(999)
print(obj.a2)  # 999
print(obj.a1)  # 11

print(Foo.a1)  # 11
print(Foo.a2)  # 报错,因为变量已经被封装到对象里了,不能直接访问


# 3
class Foo(object):
    a1 = 1
    __a2 = 2

    def __init__(self, num):
        self.num = num
        self.__salary = 1000

    def get_data(self):
        print(self.num + self.al)


obj = Foo(666)
print(obj.num)  # 666
print(obj.a1)  # 1

print(obj.__salary)  # 报错
print(obj.__a2)  # 报错

print(Foo.a1)  # 1
print(Foo.__a2)  # 报错,类都不能直接访问私有变量


# 4
class Foo(object):
    a1 = 1
    __a2 = 2

    def __init__(self, num):
        self.num = num
        self.__salary = 100
        0
        # self.a1=99

    def get_data(self):
        print(self.num + self.al)


obj1 = Foo(666)
obj2 = Foo(999)
print(obj1.num)  # 666
print(obj1.a1)  # 1

obj1.num = 18
obj1.a1 = 99

print(obj1.num)  # 18
print(obj1.a1)  # 99 先去对象中查找,没找到,会重新创建一个 self.a1=99

print(obj2.a1)  # 1
print(obj2.num + Foo.a1)  # 1000
print(obj2.num + obj1.a1)  # 1098
# 嵌套练习
class School:
    def __init__(self, address):
        self.address = address


bj = School('北京校区')
sh = School('上海校区')
sz = School('深圳校区')


class Course(object):
    def __init__(self, name, period, price, school=None):
        self.name = name
        self.period = period
        self.price = price
        self.school = school
        # self.school=None


py1 = Course('Python全栈', 110, 19999, bj)
py2 = Course('Python全栈', 110, 19999, sh)
py3 = Course('Python全栈', 110, 19999, sz)

l1 = Course('Linux运维', 110, 19999, bj)
l2 = Course('Linux运维', 110, 19999, sh)
g1 = Course('Go开发', 119, 19999, bj)


class Grade(object):
    def __init__(self, name, people, introduce, course=None):
        self.name = name
        self.people = people
        self.introduce = introduce
        self.course = course


gr1 = Grade('全栈1期', 20, '....', py1)
gr2 = Grade('全栈1期', 20, '....', py2)
gr3 = Grade('Linux8期', 20, '....', l2)

gr1.people
gr1.course.price
gr1.course.school.address

# 类和字典 的组合
class StarkConfig(object):

    def __init__(self, num):
        self.num = num

    def changelist(self, request):
        print(self.num, request)

    def run(self):
        self.changelist(999)


class RoleConfig(StarkConfig):

    def changelist(self, request):
        print(666, self.num)


class AdminSite(object):
    def __init__(self):
        self._registry = {}

    def register(self, k, v):
        self._registry[k] = v


site = AdminSite()  # 拿到一个空字典
site.register('lyd', StarkConfig(19))
site.register('yjl', StarkConfig(20))
site.register('fgz', RoleConfig(33))  # 新增三个键值对 {lyd:StarkConfig(19),....}
# print(len(site._registry)) # 3

for k, row in site._registry.items():
    row.run()  # 输出结果:19,999 20,99 666,333
    # StarkConfig(19).run()

# 变换
class UserInfo(object):
    pass
    name = "min"


class Department(object):
    pass
    name = "good"


class StarkConfig(object):
    def __init__(self, num):
        self.num = num

    def changelist(self, request):
        print(self.num, request)
        # Useinfo,999
        # print(self.num.name,request) 可以通过对象调用自己内部成员

    def run(self):
        self.changelist(999)


class RoleConfig(StarkConfig):

    def changelist(self, request):
        print(666, self.num)


class AdminSite(object):
    def __init__(self):
        self._registry = {}

    def register(self, k, v):
        self._registry[k] = v(k)


site = AdminSite()  # 空字典 
site.register(UserInfo, StarkConfig)
site.register(Department, StarkConfig)  # {UserInfo,StarkConfig(UserInfo),....}
# print(len(site._registry))
for k, row in site._registry.items():
    row.run()
    # StarkConfig(UserInfo).run()
    # self.num=Userinfo
 
# 升级题 
class UserInfo(object):
    pass


class Department(object):
    pass


class StarkConfig(object):
    def __init__(self, num):
        self.num = num  # self.num=UserInfo

    def get_vals(self):
        v = [11, 22, 33]
        extra = self.extra_vals()
        if extra:  # None 为假
            v.extend(extra)
        return v

    def extra_vals(self):
        pass

    def run(self):
        return self.get_vals()


class RoleConfig(StarkConfig):
    def extra_vals(self):
        return [99, 88]


class AdminSite(object):
    def __init__(self):
        self._registry = {}

    def register(self, k, v):
        self._registry[k] = v(k)


site = AdminSite()  # 空字典
site.register(UserInfo, StarkConfig)
site.register(Department, StarkConfig)  # {UserInfo,StarkConfig(UserInfo),....}
for k, row in site._registry.items():
    print(row.run())  # ret=StarkConfig(UserInfo).run()
    # ret=self.get_vals() 
    # 结果:[11,22,33] [11,22,33]

# 总结:
# 类加了括号(一起出现)就是对象,对象里的参数优先赋值到构造函数里实例化,
# --类调用函数,当前self就是该类
  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值