python面向对象

1.1 面向对象VS面向过程

  1、编程范式

      1. 编程是 程序 员 用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程
      2. 实现一个任务的方式有很多种不同的方式, 对这些不同的编程方式的特点进行归纳总结得出来的编程方式类别,即为编程范式。
      3. 两种最重要的编程范式分别是面向过程编程和面向对象编程。

  2、面向过程编程(Procedural Programming)

      1. 面向过程编程是程序从上到下一步步执行,一步步从上到下,从头到尾的解决问题 。
      2. 基本设计思路就是程序一开始是要着手解决一个大的问题,然后把一个大问题分解成很多个小问题或子过程
      3. 这些子过程再执行的过程再继续分解直到小问题足够简单到可以在一个小步骤范围内解决。
      4. 这样做的问题也是显而易见的,就是如果你要对程序进行修改,对你修改的那部分有依赖的各个部分你都也要跟着修改
      5. 如果程序开头你设置了一个变量值 为1 , 但如果其它子过程依赖这个值 为1的变量才能正常运行,那么这个变量1变了,依赖程序全部都要改变

def db_conn():
    print("connecting db...")

def db_backup(dbname):
    print("导出数据库...", dbname)
    print("将备份文件打包,移至相应目录...")

def db_backup_test():
    print("将备份文件导入测试库,看导入是否成功")

def main():
    db_conn()
    db_backup('my_db')
    db_backup_test()

if __name__ == '__main__':
    main()

# 运行结果:
# connecting db...
# 导出数据库... my_db
# 将备份文件打包,移至相应目录...
# 将备份文件导入测试库,看导入是否成功
使用下面伪代码理解面向过程

 

 1.2 面向对象编程主要优点 及 公有属性,私有属性,普通属性等

  1、面向对象编程主要优点:易维护,易扩展,效率高

      1. 其实OOP编程的主要作用和函数一样也是使你的代码修改和扩展变的更容易

      2. 函数编程与OOP的主要区别就是OOP可以使程序更加容易扩展和易更改。

      3. OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述

      4. 使用面向对象编程的原因一方面是因为它可以使程序维护和扩展变得更简单,并且可以大大提高程序开发效率

      5. 另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。

  2.   Class 类(模板)

      1. 一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。

      2. 在类中定义了这些对象的都具备的属性(variables(data))、共同的方法

  3、Object 对象(实例)

      1.一个对象即是一个类的实例化后实例

      2. 一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性

      3. 就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同

  4、类中的一些名词

      1.  __init__                                                   # 构造函数

      2.  Self.name = name                                  # 实例变量、普通属性  或者叫  普通字段

      3.  public_object = "public"                       # 类变量、公有属性  或则叫   静态字段

      4.  self.__heart= "Normal"                         # 私有属性 在外面无法访问

      5.  def shot(self)                                         # 类方法

class Role(object):                    #1、在定义类时继承object就是新式类,没有就是就是旧类式
    public_object = "public"           #2、在类例定义一个公有属性:所有实例都能访问的属性叫“公有属性”
    def __init__(self,name,role,weapon,life_value=100,money=15000):  #构造函数==初始化方法:模板
        self.name = name               #3、普通属性
        self.__heart= "Normal"         #4、私有属性在外面无法访问
    def shot(self):                    #5、类的方法
        print("%s is shooting..."%self.name)

  5、公有属性,普通属性,私有属性 比较

      1. 公有属性:在内存中仅存一份

      2. 普通属性:每个实例对象在内存存一份

      3. 私有属性:实例在外部无法调用

   6、类中函数私有化

       1)默认情况下,程序可以从外部访问一个对象的特性    

       2)为了让方法和特性变成私有(从外部无法访问),只要在它的名字前加上双下划线即可

       3)先在__inaccessible从外界是无法访问的,而在内部还能使用(比如从accessible访问)

class Secretive:
    def __accessible(self):
        print("you can't see me,unless you're calling internally")
    def accessible(self):
        print("The secret message is:")
        self.__accessible()
s = Secretive()
s.accessible()

# 运行结果:
# The secret message is:
# you can't see me,unless you're calling internally

 

 1.3 面向对象三大特性: 封装,继承,多态

  1、Encapsulation 封装(隐藏实现细节)

      1.在类中对数据的赋值、内部调用对外部用户是透明的

      2. 这使类变成了一个胶囊或容器,里面包含着类的数据和方法

      3. 作用:

        1)防止数据被随意修改

        2)使外部程序不需要关注对象内部的构造,只需要通过对外提供的接口进行直接访问

  2、 Inheritance 继承(代码重用)

      1. 一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承

      2. 比如CS中的警察和恐怖分子,可以将两个角色的相同点写到一个父类中,然后同时去继承它

      3. 使用经典类: Person.__init__(self,name,age) 并重写写父类Person的构造方法,实现,先覆盖,再继承,再重构

class Person(object):
    def __init__(self,name,age):  #执行Person.__init__(self,name,age)时就会将传入的参数执行一遍
        self.name = name          #所以在BlackPerson中不仅有name,age而且还有sex
        self.age = age
        self.sex = "normal"
    def talk(self):
        print("person is talking....")

class WhitePerson(Person):
    pass

class BlackPerson(Person):
    def __init__(self,name,age,strength):     #先覆盖,再继承,再重构
        #先覆盖父类的__init__方法,再继承父类__init__,再加自己的参数
        Person.__init__(self,name,age)        #先继承父类Person,这里self就是BlackPerson本身
        #先将name,age传给子类BlackPerson,然后调用Person.__init__构造方法将参数出入父类()
        self.strength = strength              #然后再重构自己的方法,即写自己的参数
        print(self.name,self.age,self.sex)
        print(self.strength)
    def talk(self):
        print("black balabla")
    def walk(self):
        print("is walking....")

b = BlackPerson("wei er smith",22,"Strong")
b.talk()
b.walk()


# 运行结果:
# wei er smith 22 normal
# Strong
# black balabla
# is walking....
# person is talking....
黑人,白人都继承父类Person就可以都有父类的属性和方法了

 

  3、Polymorphism 多态(接口重用)

      1.多态是面向对象的重要特性,简单点说:“一个接口,多种实现”

      2. 指一个基类中派生出了不同的子类,且每个子类在继承同样的方法名的同时又对父类的方法做了不同的实现

      3. 这就是同一种事物表现出的多种形态

      4. 比如黄种人继承了人talk这个功能,但是他说的是中文,而美国人的talk是英文,但是他们是同样的talk

      作用:简单的讲就是允许父类调用子类的方法

class Animal:
    def __init__(self, name):    # Constructor of the class
        self.name = name
    def talk(self):              # Abstract method, defined by convention only
        raise NotImplementedError("Subclass must implement abstract method")

class Cat(Animal):
    def talk(self):
        return 'Meow!'

class Dog(Animal):
    def talk(self):
        return 'Woof! Woof!'

animals = [Cat('Missy'),
           Dog('Lassie')]

for animal in animals:
    print(animal.name + ': ' + animal.talk())

# 运行结果:
# Missy: Meow!
# Lassie: Woof! Woof!
多态举例

 

1.4 面向对象:组合----功能类似继承

   1、组合的作用

      1. 和继承很相似,可以让在一个类中使用另一个类的方法,但是并不是父类和子类的继承关系

      2. 他是先实例化一个类的实例,然后将这个实例当做参数传入列一个类中,所以就可以在这个类中调用了

   2、组合举例说明

      1. 如何实现Teacher类不是SchoolMember类的子类的情况下而在Teacher类中调用SchoolMember的方法

      2. 方法很简单:首先实例化一个SchoolMember的实例,然后将这个实例当做参数传入到Teacher类中

      3. 那么在Teacher类中通过 ***.fangfa  就可以调用了

      4. 这里 T1.user_SM() 就是调用了SchoolMember中的tell方法

class SchoolMember(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def tell(self):
        print("I am SchoolMember,my name is %s"%self.name)
SM1 = SchoolMember("tom",100)

class Teacher(object):
    def __init__(self,sex,t1):
        self.SchoolMember = t1
    def user_SM(self):
        self.SchoolMember.tell()
T1 = Teacher('F',SM1)
T1.user_SM()
组合实例:Teacher类中调用SchoolMember类的方法

 

 1.5 创建一个类,并实例化后在内存中怎样存储的

   1、创建一个最简单的Dog类,实例化并调用他的sayhi()方法,这个过程的原理

class Dog(object):
    def __init__(self,name,dog_type):
        self.name = name
        self.type = dog_type
    def sayhi(self):
        print("i am a dog,my name is %s"%self.name)
d = Dog('LiChuang','京巴')   #等价于  Dog(d,"LiChuang","京巴")
d.sayhi()                   #运行结果:i am a dog,my name is LiChuang

  2、为什么可以在sayhi()类方法中可以调用self.name

      1)在实例化时:d = Dog('LiChuang','京巴')等价于d = Dog(d,'LiChuang','京巴')将实例d自己传递给类中self参数,

        所以self.name = name  等价于 d.name = name

      2)在调用类方法d.sayhi()的时候相当于调用d.sayhi(d),上一步实例化时已经将d.name = name(赋值给了name)

      3)所以在类方法中调用self.name就是调用d.name, self传递给了sayhi()所以可以调用self.name

  3. 实例化d = Dog('LiChuang','京巴')在内存中是怎样存储的?

      1)当一个类被定义后,就会开辟一块内存空间,存放着类自己(可以理解为一个模板)

      2)执行d = Dog('LiChuang','京巴')时是实质上需要执行以下几步:

        第一步:先申请一块内存空间,命名为d

        第二步:将第一步中d的地址空间,d实例和‘LiChuang’ ‘京巴’两个参数都传递给类自己(也就是模板)

        第三步:刚刚将实例d已经传进去了,所以可以执行d.name = LiChuang, d.type = 京巴,然后将这两个值返回到刚刚实例d申请的内存中

                  

1.6 新式类 与 经典类 及 重写构造方法 区别举例

  1、 新式类和经典类区别(三种)

      1)首先,写法不一样:

        class A:                       #经典类写法

          pass

        class B(object):          #新式类写法

          pass

      2)多继承中,新式类采用广度优先搜索,而旧式类是采用深度优先搜索,python3中全是广度查询

      3)在继承中新式类和经典类写法区别

        SchoolMember.__init__(self,name,age,sex)                      #经典类写法
        super(Teacher,self).__init__(name,age,sex)                     #新式类写法

                             

   2、重写特殊的构造方法      

      1. 注:如果一个类的构造方法被重写,那么就需要调用超类的构造方法,否则对象可能不能给被正确的初始化

      2. 其实调用超类构造方法很容易,SongBird类中只添加一行代码

          经典类写法:    Bird.__init__(self)

          新式类写法:    super(SongBird,self).__init__()

      3. 如果将下面代码没有2中的那句调用父类Bird中的self.hungry方法会报错

              if self.hungry == True:

          AttributeError: SongBird instance has no attribute 'hungry'

class Bird:
    def __init__(self):
        self.hungry = True
    def eat(self):
        if self.hungry == True:
            print("Aaaah...")
            self.hungry = False
        else:
            print("no, thanks")
a = Bird()
a.eat()
a.eat()

class SongBird(Bird):
    def __init__(self):
        # Bird.__init__(self)                            # 经典类写法
        # super(SongBird,self).__init__()            # 新式类写法
        self.sound = 'Squawk'
    def sing(self):
        print(self.sound)
b = SongBird()
b.sing()
b.eat()
例:新式类,经典类,无超类构造方法报错

 

 1.7 静态方法、类方法、属性方法

  1、静态方法

      1. 作用:静态方法可以更好的组织代码,防止代码变大后变得比较混乱。  

      2. 特性:  静态方法只是名义上归类管理,实际上在静态方法里访问不了类或则实例中的任何属性

      3. 静态方法使用场景:

        1)我们要写一个只在类中运行而不在实例中运行的方法.
        2)经常有一些跟类有关系的功能但在运行时又不需要实例和类参与的情况下需要用到静态方法.
        3)比如更改环境变量或者修改其他类的属性等能用到静态方法.
        4)这种情况可以直接用函数解决, 但这样同样会扩散类内部的代码,造成维护困难.

       4. 调用方式:  既可以被类直接调用,也可以通过实例调用

class Dog(object):
    def __init__(self,name):
        self.name = name
    @staticmethod
    def eat():
        print("I am a static method")
d = Dog("ChenRonghua")
d.eat()                       #方法1:使用实例调用
Dog.eat()                   #方法2:使用类直接调用
@staticmethod静态方法

 

  2、类方法

      1. 作用:无需实例化直接被类调用  

      2. 特性:  类方法只能访问类变量,不能访问实例变量

      3. 类方法使用场景: 当我们还未创建实例,但是需要调用类中的方法

      4. 调用方式:  既可以被类直接调用,也可以通过实例调用

class Dog(object):
    name = '类变量' #在这里如果不定义类变量仅定义实例变量依然报错
    def __init__(self,name):
        self.name = '实例变量'
        self.name = name
    @classmethod
    def eat(self,food):
        print("%s is eating %s"%(self.name,food))
Dog.eat('baozi')                   #方法1:使用类直接调用
d = Dog("ChenRonghua")          
d.eat("包子")                      #方法2:使用实例d调用
@classmethod类方法

 

  3、属性方法

      作用:属性方法把一个方法变成一个属性,隐藏了实现细节,调用时不必加括号直接d.eat即可调用self.eat()方法

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

    @property
    def eat(self):
        print(" %s is eating" % self.name)
d = Dog("ChenRonghua")
d.eat()
# 调用会出以下错误, 说NoneType is not callable, 因为eat此时已经变成一个静态属性了, 
# 不是方法了, 想调用已经不需要加()号了,直接d.eat就可以了
@property属性方法

 

 1.8 类的特殊成员方法: __str__  __call__等

  1、__doc__  表示类的描述信息

class Foo:
    """ 输出类的描述类信息 """
    def func(self):
        pass
print(Foo.__doc__) #运行结果:描输出类的描述类信息

 

  2、__call__ 对象后面加括号,触发执行。

      作用:构造方法__init__的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后

                括号触发的,即:对象() 或者 类()()      执行__call__方法

class Dog(object):
    def __init__(self,name):
        self.name = '实例变量'
        self.name = name
    def __call__(self, *args, **kwargs):
        print("running call")
        print(args,kwargs)
d = Dog("ChenRonghua")
d(name='tom')               # 如果只实例化,而不d()调用,__call__函数不会执行

# 运行结果:
# running call
# () {'name': 'tom'}

 

  3. __str__ 如果一个类中定义了__str__方法,在打印对象时,默认输出该方法的返回值

class Foo:
    def __str__(self):
        return 'alex li'
obj = Foo()
print(obj)     # 输出:alex li

 

  4. __dict__ 查看类或对象中的所有成员

class Province:
    country = 'China'
    def __init__(self, name, count):
        self.name = name
        self.count = count
    def func(self, *args, **kwargs):
        print('func')
# 获取类的成员,即:静态字段、方法、
print(Province.__dict__)
# 输出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None}

obj1 = Province('HeBei',10000)
print(obj1.__dict__)
# 获取 对象obj1 的成员
# 输出:{'count': 10000, 'name': 'HeBei'}

obj2 = Province('HeNan', 3888)
print(obj2.__dict__)
# 获取 对象obj1 的成员
# 输出:{'count': 3888, 'name': 'HeNan'}

 

  5. __getitem__、__setitem__、__delitem__

      作用:用于索引操作,如字典。以上分别表示获取、设置、删除数据

class Foo(object):
    def __getitem__(self, key):
        print('__getitem__', key)

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

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

obj = Foo()
result = obj['k1']           # __getitem__ k1
obj['k2'] = 'wupeiqi'        # __setitem__ k2 wupeiqi
del obj['k1']                # __delitem__ k1

 

  6. __new__ \ __metaclass__ 

      作用: __metaclass__定义这个类以怎样的形式被创建

class User(object):
    def __init__(self,name,age):
        print('__init__')

    def __new__(cls, *args, **kwargs):
        print("__new__",args,kwargs)
        return object.__new__(cls)         # 调用一下object的__new__方法否则不往下走
d = User('tom',100)

# 运行结果:
# __new__ ('tom', 100) {}
# __init__

 

  7. __new__和__init__的区别

      1、 __new__是一个静态方法,而__init__是一个实例方法.
      2、 __new__方法会返回一个创建的实例,而__init__什么都不返回.
      3、 只有在__new__返回一个cls的实例时后面的__init__才能被调用.
      4、 当创建一个新实例时调用__new__,初始化一个实例时用__init__.

1.9 type生成类(__metaclass__)

   1、type生成类调用顺序

      1) __new__  :  先于__init__方法,每生成一个实例执行一次

      2) __init__   :  __init__方法每生成一个实例对象就会执行一次

      3) __call__  :   后与__init__方法,C()() 使用类再加一个括号调用, C为类名称

      4)__del__   :   析构方法,删除无用的内存对象(当程序结束会自动自行析构方法)

class Student(object):
    def __new__(cls, *args, **kwargs):
        print('__new__')
        return object.__new__(cls)    # 必须返回父类的__new__方法,否则不不执行__init__方法,无法创建实例

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

    def __str__(self):                # 作用:打印实例时显示指定字符串,而不是内存地址
        print('__str__')
        return self.name

    def __call__(self, *args, **kwargs):        # 当执行C()(*args) 或者 s1(*args) 就会执行__call__
        print('__call__',*args)

    def __del__(self):                # 作用:清除无用的实例对内存的暂用
        print('__del__')

#1、实例化时机会执行__new__、__init__
s1 = Student('tom')

#2、执行 实例()  就会执行__call__ 方法,并将参数传递给__call__函数
s1('call01')

#3、当打印实例时就会执行 __str__ 方法下返回的字符串(默认返回的实例地址)
print(s1)

#4、析构方法:当删除实例时就会调用 __del__ 方法
del s1
# 析构方法作用:在程序结束后会自动执行析构方法删除所有实例
# 但是在程序运行时有很多实例是无用的,但是python内存回收机制却不会自动删除他们,这样就浪费内存
# 我们可以执行 del s1 ,那么在程序运行时,python内存回收机制会检测到这些实例时无用的,才会删除
# 其实我们执行del s1,并没有回收内存,只不过是摘除门牌号,python内存回收机制发现没有门牌号后会自动回收内存
类实例化过程这些魔法方法被调用的顺序与触发方式

 

   2、普通方式创建类过程理解

class Foo(object):
    def __init__(self,name):
        self.name = name
f = Foo("alex")

      1. 上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象

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

        是通过执行某个类的 构造方法 创建。

          print type(f)                 # 输出:<class '__main__.Foo'>  表示,obj 对象由Foo类创建

          print type(Foo)            # 输出:<type 'type'>      表示,Foo类对象由 type 类创建

      3. 所以,f对象是Foo类的一个实例Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。

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

      5. 答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可为 __metaclass__

         设置一个type类的派生类,从而查看 类 创建的过程

                       

  2、type创建无构造方法的类

def func(self):
    print('hello world')
Foo = type('Foo', (object,), {'func': func,'city':'bj'})

obj = Foo()
obj.func()                    # hello wupeiqi
print(obj.city)              # bj

# type第一个参数:类名
# type第二个参数:当前类的基类(继承的类)
# type第三个参数:类的成员(比如内中有的方法)


#注:上面使用type生成的类就是这样的
class Foo(obj):
    city = 'bj'
    def func(self):
        print('hello world')

 

  3、type创建有构造方法的类

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

def sayName(self):
    print("hello I am %s"%self.name)

def sayAge(self):
    print("hello I am %s"%self.age)

Foo = type('Foo',(object,),{'sayName':sayName,'__init__':__init__})
# type第一个参数:类名
# type第二个参数:当前类的基类(继承的类)
# type第三个参数:类的成员(比如内中有的方法)

setattr(Foo,'sayAge',sayAge)        # 使用setattr添加一个类方法

f = Foo("jack",22)
f.sayName()               # hello I am jack
f.sayAge()                # hello I am 22

#注:上面使用type生成的类就是这样的
class Foo(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def sayName(self):
        print("hello I am %s" % self.name)

    def sayAge(self):
        print("hello I am %s" % self.age)

 

  4、使用type动态生成ModelForm类举例

def create_model_form(request,admin_class):
   def __new__(cls,*args,**kwargs):                        #继承ModelForm的__new__方法
      pass
   def default_clean(self):
      pass
   class Meta:                                             #ModelForm中使用Meta类进行条件过滤
      model = admin_class.model                            #model指定关联那个类
      fields = "__all__"                                  #仅显示那些字段
      exclude = admin_class.modelform_exclude_fields       #那些字段不显示
   attrs = {'Meta':Meta}

   #这个生成的ModelForm类继承ModelForm类,可以用来做ModelForm验证
   _model_form_class = type("DynamicModelForm",(ModelForm,),attrs)       #创建类并设置Meta属性
   setattr(_model_form_class,"__new__",__new__)                           #动态将__new__函数添加到类中
   setattr(_model_form_class,'clean',default_clean)                       #动态将_default_clean__函数添加到类中

 

 1.10 反射: hasattr、getattr、setattr 和 delattr

   1. hasattr(ogj,name_str) 判断一个对象里是否有对应的字符串方法

class Dog(object):
    def eat(self,food):
        print("eat method!!!")
d = Dog()

#hasattr判断对象d是否有eat方法,有返回True,没有返回False
print(hasattr(d,'eat'))     #True
print(hasattr(d,'cat'))     #False

 

   2. getattr(obj,name_str) 根据字符串去获取obj对象里的对应的方法对应的内存地址

class Dog(object):
    def eat(self):
        print("eat method!!!")
d = Dog()

if hasattr(d,'eat'):          # hasattr判断实例是否有eat方法
    func = getattr(d, 'eat')  # getattr获取实例d的eat方法内存地址
    func()                    # 执行实例d的eat方法
#运行结果:  eat method!!!

 

  3. 使用stattr给类实例对象动态添加一个新的方法

class Dog(object):
    def eat(self,food):
        print("eat method!!!")
d = Dog()

def bulk(self):               #给Dog类添加一个方法必须先写这个方法
    print('bulk method add to Dog obj')

d = Dog()
setattr(d,"bulk",bulk)        #将bulk方法添加到实例d中,命名为talk方法
d.bulk(d)                     #实例d调用刚刚添加的talk方法时必须将实例d自身当变量传入,因为他不会自己传入self

#1. 注:setattr(x,’y’,z)用法:    x就是实例对象  y就是在实例中调用时用的名字,z就是改变属性的值/或则要添加的函数的名字(正真的函数)
#2. setattr( 具体实例名称 , ’在类中调用时使用的名称’ , 要添加的真实函数的名称)
#3. 作用: setattr(d,"bulk",bulk) 将bulk方法添加到实例d中并且在实例中以bulk名称调用

 

  4. delattr删除实例属性

class Dog(object):
    def __init__(self,name):
        self.name = name
    def eat(self,food):
        print("%s is eating....."%self.name)
d = Dog("NiuHanYang")
choice = input(">>:").strip()
if hasattr(d,choice):
    delattr(d,choice)  #使用delattr(d,choice)删除实例的属性那么所以下面打印就会报错
print(d.name)

# 运行结果:
# >>:name   #输入的值是name
# 下面是报错信息
# Traceback (most recent call last):
#   File "C:/Users/admin/PycharmProjects/s14/Day7/test1.py", line 10, in <module>

 

posted @ 2019-08-25 18:48  darkly  阅读(204)  评论(0编辑  收藏  举报