Python初识对象

Posted dongye95

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python初识对象相关的知识,希望对你有一定的参考价值。

一 楔子

你现在是一家游戏公司的开发人员,现在需要你开发一款叫做<人狗大战>的游戏,你就思考呀,人狗作战,那至少需要2个角色,一个是人, 一个是狗,且人和狗都有不同的技能,比如人拿棍打狗, 狗可以咬人,怎么描述这种不同的角色和他们的功能呢?
 
你搜罗了自己掌握的所有技能,写出了下面的代码来描述这两个角色
def person(name,age,sex,job):
    data = {
        \'name\':name,
        \'age\':age,
        \'sex\':sex,
        \'job\':job
    }
 
    return data
 
def dog(name,dog_type):
    data = {
        \'name\':name,
        \'type\':dog_type
    }
    return data

上面两个方法相当于造了两个模子,游戏里的每个人和每条狗都拥有相同里的属性。游戏开始,你根据一个人或一只狗传入的具体信息来塑造一个具体的人或者狗,怎么生成呢?

d1 = dog("李磊","京巴")
p1 = person("严帅",36,"F","运维")
p2 = person("egon",27,"F","Teacher"

两个角色对象生成了,狗和人还有不同的功能呀,狗会咬人,人会打狗,对不对? 怎么实现呢,。。想到了, 可以每个功能再写一个函数,想执行哪个功能,直接 调用 就可以了,对不?

def bark(d):
    print("dog %s:wang.wang..wang..."%d[\'name\'])
 
 
def walk(p):
    print("person %s is walking..." %p[\'name\'])<br><br>

walk(p1) 
bark(d1)

上面的功能实现的简直是完美!

但是仔细玩耍一会,你就不小心干了下面这件事

p1 = person("严帅",36,"F","运维")
bark(p1) #把人的对象传给了狗的方法

事实 上,从你写的代码上来看,这并没出错。很显然,人是不能调用狗的功能的,但在你的程序例没有做限制,如何在代码级别实现这个限制呢?

限制功能的全新代码

def person(name,age,sex,job):
    def walk(p):
        print("person %s is walking..." % p[\'name\'])
 
    data = {
        \'name\':name,
        \'age\':age,
        \'sex\':sex,
        \'job\':job,
        \'walk\':walk
    }
 
    return data
 
def dog(name,dog_type):
 
 
    def bark(d):
        print("dog %s:wang.wang..wang..."%d[\'name\'])
    data = {
        \'name\':name,
        \'type\':dog_type,
        \'bark\':bark
    }
 
    return data

生成具体的狗和人

d1 = dog("李磊","京巴")
p1 = person("严帅",36,"F","运维")
p2 = person("egon",27,"F","Teacher")

无法调用

d1[\'bark\'](p1) #把人传给了狗的方法

你是如此的机智,这样就实现了限制人只能用人自己的功能啦。

刚才你只是阻止了两个完全 不同的角色 之前的功能混用, 但有没有可能 ,同一个种角色,但有些属性是不同的呢? 比如 ,大家都打过cs吧,cs里有警察和恐怖份子,但因为都 是人, 所以你写一个角色叫 person(), 警察和恐怖份子都 可以 互相射击,但警察不可以杀人质,恐怖分子可以,这怎么实现呢? 你想了说想,说,简单,只需要在杀人质的功能里加个判断,如果是警察,就不让杀不就ok了么。 没错, 这虽然 解决了杀人质的问题,但其实你会发现,警察和恐怖分子的区别还有很多,同时又有很多共性,如果 在每个区别处都 单独做判断,那得累死。 

你想了想说, 那就直接写2个角色吧, 反正 这么多区别, 我的哥, 不能写两个角色呀,因为他们还有很多共性 , 写两个不同的角色,就代表 相同的功能 也要重写了。

二、面向过程 VS 面向对象

面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。

优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。

缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。

应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。

 

面向对象的程序设计

优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。

缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题即便是上帝也无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,这个游戏就失去平衡。

应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。

面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。

 

了解一些名词:类、对象、实例、实例化

类:具有相同特征的一类事物(人、狗、老虎)

对象/实例:具体的某一个事物(隔壁阿花、楼下旺财)

实例化:类——>对象的过程(这在生活中表现的不明显,我们在后面再慢慢解释)

三、初识类和对象

python中一切皆为对象,类型的本质就是类,所以,不管你信不信,你已经使用了很长时间的类了

>>> dict #类型dict就是类dict
<class \'dict\'>
>>> d=dict(name=\'eva\') #实例化
>>> d.pop(\'name\') #向d发一条消息,执行d的方法pop
\'eva\'

  从上面的例子来看,字典就是一类数据结构,我一说字典你就知道是那个用{}表示,里面由k-v键值对的东西,它还具有一些增删改查的方法。但是我一说字典你能知道字典里具体存了哪些内容么?不能,所以我们说对于一个类来说,它具有相同的特征属性和方法。

  而具体的{\'name\':\'eva\'}这个字典,它是一个字典,可以使用字典的所有方法,并且里面有了具体的值,它就是字典的一个对象。对象就是已经实实在在存在的某一个具体的个体。

  在python中,用变量表示特征,用函数表示技能,因而具有相同特征和技能的一类事物就是‘类’,对象是则是这一类事物中具体的一个。

3.1 类的相关知识

3.1.1 初识类

3.1.1.1 声明

声明函数

def functionName(args):
     \'函数文档字符串\'
      函数体 

声明类

\'\'\'
class 类名:
    \'类的文档字符串\'
    类体
\'\'\'

#我们创建一个类
class Data:
    pass

-

class Person:   #定义一个人类
    role = \'person\'  #人的角色属性都是人
    def walk(self):  #人都可以走路,也就是有一个走路方法,也叫动态属性
        print("person is walking...")

3.1.2 类有两种作用:属性引用和实例化

3.1.2.1 属性引用(类名.属性)

class Person:   #定义一个人类
    role = \'person\'  #人的角色属性都是人
    def walk(self):  #人都可以走路,也就是有一个走路方法
        print("person is walking...")


print(Person.role)  #查看人的role属性
print(Person.walk)  #引用人的走路方法,注意,这里不是在调用

3.1.2.2 实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征

class Person:   #定义一个人类
    role = \'person\'  #人的角色属性都是人
    def __init__(self,name):
        self.name = name  # 每一个角色都有自己的昵称;
        
    def walk(self):  #人都可以走路,也就是有一个走路方法
        print("person is walking...")


print(Person.role)  #查看人的role属性
print(Person.walk)  #引用人的走路方法,注意,这里不是在调用

实例化的过程就是类——>对象的过程

原本我们只有一个Person类,在这个过程中,产生了一个egg对象,有自己具体的名字、攻击力和生命值。

语法:对象名 = 类名(参数)

egg = Person(\'egon\')  #类名()就等于在执行Person.__init__()
#执行完__init__()就会返回一个对象。这个对象类似一个字典,存着属于这个人本身的一些属性和方法。

3.1.2.3 查看属性&调用方法

print(egg.name)     #查看属性直接 对象名.属性名
print(egg.walk())   #调用方法,对象名.方法名()

3.1.2.4 关于self

self:在实例化时自动将对象/实例本身传给__init__的第一个参数,你也可以给他起个别的名字,但是正常人都不会这么做。

3.1.2.5 类属性的补充

一:我们定义的类的属性到底存到哪里了?有两种方式查看
dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值

二:特殊的类属性
类名.__name__# 类的名字(字符串)
类名.__doc__# 类的文档字符串
类名.__base__# 类的第一个父类(在讲继承时会讲)
类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
类名.__dict__# 类的字典属性
类名.__module__# 类定义所在的模块
类名.__class__# 实例对应的类(仅新式类中)

3.2 对象的相关知识

回到咱们的人狗大战:现在我们需要对我们的类做出一点点改变
人类除了可以走路之外,还应该具备一些攻击技能。

class Person:  # 定义一个人类
    role = \'person\'  # 人的角色属性都是人

    def __init__(self, name, aggressivity, life_value):
        self.name = name  # 每一个角色都有自己的昵称;
        self.aggressivity = aggressivity  # 每一个角色都有自己的攻击力;
        self.life_value = life_value  # 每一个角色都有自己的生命值;

    def attack(self,dog):  
        # 人可以攻击狗,这里的狗也是一个对象。
        # 人攻击狗,那么狗的生命值就会根据人的攻击力而下降
        dog.life_value -= self.aggressivity

对象是关于类而实际存在的一个例子,即实例

对象/实例只有一种作用:属性引用

egg = Person(\'egon\',10,1000)
print(egg.name)
print(egg.aggressivity)
print(egg.life_value)

当然了,你也可以引用一个方法,因为方法也是一个属性,只不过是一个类似函数的属性,我们也管它叫动态属性。
引用动态属性并不是执行这个方法,要想调用方法和调用函数是一样的,都需要在后面加上括号

print(egg.attack)

我知道在类里说,你可能还有好多地方不能理解。那我们就用函数来解释一下这个类呀,对象呀到底是个啥,你偷偷的用这个理解就好了,不要告诉别人

def Person(*args,**kwargs):
    self = {}
    def attack(self,dog):
        dog[\'life_value\'] -= self[\'aggressivity\']

    def __init__(name,aggressivity,life_value):
        self[\'name\'] = name
        self[\'aggressivity\'] = aggressivity
        self[\'life_value\'] = life_value
        self[\'attack\'] = attack

    __init__(*args,**kwargs)
    return self

egg = Person(\'egon\',78,10)
print(egg[\'name\'])

 

class Person:                 # 类名
    country = \'China\'         # 创造了一个只要是这个类就一定有的属性
                               # 类属性 静态属性
    def __init__(self,*args):  # 初始化方法,self是对象,是一个必须传的参数
        # self就是一个可以存储很多属性的大字典
        self.name = args[0]   # 往字典里添加属性的方式发生了一些变化
        self.hp = args[1]
        self.aggr = args[2]
        self.sex = args[3]

    def walk(self,n):         # 方法,一般情况下必须传self参数,且必须写在第一个
                              # 后面还可以传其他参数,是自由的
        print(\'%s走走走,走了%s步\'%(self.name,n))

# print(Person.country)        # 类名 可以查看类中的属性,不需要实例化就可以查看
alex = Person(\'狗剩儿\',100,1,\'不详\')  # 类名还可以实例化对象,alex对象   # 实例化
# print(alex.__dict__) # 查看所有属性
print(alex.name)  # 查看属性值
# print(alex.hp)  # 查看属性值
alex.walk(5)    # Person.walk(alex,5)  # 调用方法 类名.方法名(对象名)

print(Person.__dict__[\'country\'])
Person.__dict__[\'country\'] = \'印度\'
print(alex.__dict__[\'name\'])
alex.__dict__[\'name\'] = \'二哥\'
print(alex.__dict__)
print(alex.name)
print(alex.name)
alex.name = \'二哥\'
alex.__dict__[\'name\'] = \'二哥\'
alex.age = 83
print(alex.__dict__)
print(alex.name)

对象 = 类名()
过程:
    类名() 首先 会创造出一个对象,创建了一个self变量
    调用init方法,类名括号里的参数会被这里接收
    执行init方法
    返回self
对象能做的事:
    查看属性
    调用方法
    __dict__ 对于对象的增删改查操作都可以通过字典的语法进行
类名能做的事:
    实例化
    调用方法 : 只不过要自己传递self参数
    调用类中的属性,也就是调用静态属性
    __dict__ 对于类中的名字只能看 不能操作
一些解释性的代码

面向对象小结——定义及调用的固定模式

小结

class 类名:
    def __init__(self,参数1,参数2):
        self.对象的属性1 = 参数1
        self.对象的属性2 = 参数2

    def 方法名(self):pass

    def 方法名2(self):pass

对象名 = 类名(1,2)  #对象就是实例,代表一个具体的东西
                  #类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法
                  #括号里传参数,参数不需要传self,其他与init中的形参一一对应
                  #结果返回一个对象
对象名.对象的属性1   #查看对象的属性,直接用 对象名.属性名 即可
对象名.方法名()     #调用类中的方法,直接用 对象名.方法名() 即可

练一练

练习一:在终端输出如下信息

小明,10岁,男,上山去砍柴
小明,10岁,男,开车去东北
小明,10岁,男,最爱大保健
老李,90岁,男,上山去砍柴
老李,90岁,男,开车去东北
老李,90岁,男,最爱大保健
老张…

-

class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
    def shangshan(self):
        print(\'%s,%s岁,%s,上山去砍柴\'%(self.name,self.age,self.sex))
    def drive(self):
        print(\'%s,%s岁,%s,开车去东北\'%(self.name,self.age,self.sex))
    def favor(self):
        print(\'%s,%s岁,%s,最爱大保健\'%(self.name,self.age,self.sex))

 

3.3 对象之间的交互

现在我们已经有一个人类了,通过给人类一些具体的属性我们就可以拿到一个实实在在的人。
现在我们要再创建一个狗类,狗就不能打人了,只能咬人,所以我们给狗一个bite方法。
有了狗类,我们还要实例化一只实实在在的狗出来。
然后人和狗就可以打架了。现在我们就来让他们打一架吧!

3.3.1 创建一个狗类

class Dog:  # 定义一个狗类
    role = \'dog\'  # 狗的角色属性都是狗

    def __init__(self, name, breed, aggressivity, life_value):
        self.name = name  # 每一只狗都有自己的昵称;
        self.breed = breed  # 每一只狗都有自己的品种;
        self.aggressivity = aggressivity  # 每一只狗都有自己的攻击力;
        self.life_value = life_value  # 每一只狗都有自己的生命值;

    def bite(self,people):
        # 狗可以咬人,这里的狗也是一个对象。
        # 狗咬人,那么人的生命值就会根据狗的攻击力而下降
     people.life_value -= self.aggressivit

3.3.2 实例化一只实实在在的二哈

ha2 = Dog(\'二愣子\',\'哈士奇\',10,1000)  #创造了一只实实在在的狗ha2

3.3.3 交互 egon打ha2一下

print(ha2.life_value)         #看看ha2的生命值
egg.attack(ha2)               #egg打了ha2一下
print(ha2.life_value)         #ha2掉了10点血

3.3.4 完整的代码

class Person:  # 定义一个人类
    role = \'person\'  # 人的角色属性都是人

    def __init__(self, name, aggressivity, life_value):
        self.name = name  # 每一个角色都有自己的昵称;
        self.aggressivity = aggressivity  # 每一个角色都有自己的攻击力;
        self.life_value = life_value  # 每一个角色都有自己的生命值;

    def attack(self,dog):
        # 人可以攻击狗,这里的狗也是一个对象。
        # 人攻击狗,那么狗的生命值就会根据人的攻击力而下降
        dog.life_value -= self.aggressivity

class Dog:  # 定义一个狗类
    role = \'dog\'  # 狗的角色属性都是狗

    def __init__(self, name, breed, aggressivity, life_value):
        self.name = name  # 每一只狗都有自己的昵称;
        self.breed = breed  # 每一只狗都有自己的品种;
        self.aggressivity = aggressivity  # 每一只狗都有自己的攻击力;
        self.life_value = life_value  # 每一只狗都有自己的生命值;

    def bite(self,people):
        # 狗可以咬人,这里的狗也是一个对象。
        # 狗咬人,那么人的生命值就会根据狗的攻击力而下降
        people.life_value -= self.aggressivity

egg = Person(\'egon\',10,1000)  #创造了一个实实在在的人egg
ha2 = Dog(\'二愣子\',\'哈士奇\',10,1000)  #创造了一只实实在在的狗ha2
print(ha2.life_value)         #看看ha2的生命值
egg.attack(ha2)               #egg打了ha2一下
print(ha2.life_value)         #ha2掉了10点血
egon大战哈士奇
from math import pi

class Circle:
    \'\'\'
    定义了一个圆形类;
    提供计算面积(area)和周长(perimeter)的方法
    \'\'\'
    def __init__(self,radius):
        self.radius = radius

    def area(self):
         return pi * self.radius * self.radius

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


circle =  Circle(10) #实例化一个圆
area1 = circle.area() #计算圆面积
per1 = circle.perimeter() #计算圆周长
print(area1,per1) #打印圆面积和周长
一个简单的例子

3.4 类命名空间与对象、实例的命名空间

创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性

而类有两种属性:静态属性和动态属性

  • 静态属性就是直接在类中定义的变量
  • 动态属性就是定义在类中的方法

3.4.1 其中类的数据属性是共享给所有对象的

>>>id(egg.role)
4341594072
>>>id(Person.role)
4341594072

3.4.2 而类的动态属性是绑定到所有对象的

>>>egg.attack
<bound method Person.attack of <__main__.Person object at 0x101285860>>
>>>Person.attack
<function Person.attack at 0x10127abf8> 

创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性

在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常

# Course.language = \'English\'
# Course.__dict__[\'language\'] = \'Chinese\'
# print(Course.language)
python = Course(\'egon\',\'python\',\'6 months\',20000)
linux = Course(\'oldboy\',\'linux\',\'6 months\',20000)
#[\'chinese\']
python.language = \'\'
# print(python.language)
# print(linux.language)
# Course.language = \'Chinese\'
# print(python.language)
# print(linux.language)

# del python.language
# print(python.language)
# print(python.__dict__)
# print(Course.language)
# print(linux.language)
# print(linux.__dict__)
# 类中的静态变量 可以被对象和类调用
# 对于不可变数据类型来说,类变量最好用类名操作
# 对于可变数据类型来说,对象名的修改是共享的,重新赋值是独立的
一些解释性的代码

-

模拟人生
# class Person:
#     money = 0
#     def work(self):
#         Person.money += 1000

# mother = Person()
# father = Person()
# Person.money += 1000
# Person.money += 1000
# print(Person.money)
# mother.work()
# father.work()


# 创建一个类,每实例化一个对象就计数
# 最终所有的对象共享这个数据
# class Foo:
#     count = 0
#     def __init__(self):
#         Foo.count += 1

# f1 = Foo()
# f2 = Foo()
# print(f1.count)
# print(f2.count)
# f3 = Foo()
# print(f1.count)


# 认识绑定方法
# def func():pass
# print(func)

# class Foo:
#     def func(self):
#         print(\'func\')
#     def fun1(self):
#         pass
# f1 = Foo()
# print(Foo.func)
# print(f1.func)
# print(f1.fun1)
# <bound method Foo.func of f1>

# 包 —— __init__
# import package —— 类的实例化的过程
# import time
# time.time()


# 类里的名字有 类变量(静态属性量)+ 方法名(动态属性)
# 对象里的名字 对象属性
# 对象 —— > 类
# 对象找名字 : 先找自己的 找类的 再找不到就报错
# 对象修改静态属性的值
#     对于不可变数据类型来说,类变量最好用类名操作
#     对于可变数据类型来说,对象名的修改是共享的,重新赋值是独立的

 

3.5 面向对象的组合用法

软件重用的重要方式除了继承之外还有另外一种方式,即:组合

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

class Weapon:
    def prick(self, obj):  # 这是该装备的主动技能,扎死对方
        obj.life_value -= 500  # 假设攻击力是500

python  初识面向对象

python -- 面向对象初识

Python基础之(面向对象初识)

骑士计划-python全栈 15 面对对象初识

Python 面向对象初识

Python 入门 之 初识面向对象