python,类和对象的基础知识

一,定义

类:对一群具有相同特征或者行为事务的统称,是抽象的,不能直接使用,类的特征,被称为属性(就是这类事务有什么特征),行为被称为方法(就是这类事务具有什么样的行为)(函数)

        eg: 造飞机的图纸就是一个类,是一个模板,负责创建对象的

对象: 由类创建出来的一个具体的存在,可以直接使用,由哪个类创建出来的对象,就拥有那个类中定义的所有属性和方法。

          eg:由飞机图纸造出来的飞机,就是飞机图纸类的对象,是具体存在的,可直接使用

类和对象之间的关系: 先有类,后有对象。类只有一个,对象可以有很多个,不同对象之间的某些属性可能不同,例如,狗a,和狗b,名字这个属性就不一样了。类有的属性和方法,对象都有。

二:分析

在面向对象的开发过程中,应先做需求分析,确定程序中需要包含哪些类,而且,要被先用到的类要先开发!

可以使用名词提炼法-- 分析整个业务流程,出现的名词,,一般来说就是要找的类。

例子: 小明,19岁,身高 175.早上会去跑步,吃东西
           小美,20岁,身高155,也会去跑步,吃东西
名词提炼法: 都是人名 ----定义一个 person类
属性:  年龄(age),身高(high),名字(name)
方法: 动词,跑步(run方法),吃饭(eat方法)

类的命名规则:

必须要符合大驼峰命名法,即每个单词首字母大写,单词之间不用下划线连接。

 

面向对象的开发过程:

1:设计类

2:使用类名()创建对象,其中包含以下两部

2.1:在内存中为类对象分配空间

2.2:调用初始化方法__init__为对象初始化

3:对象创建后,内存中就有一个对象实际存在 ---- 实例

因此:创建出来的对象叫做类的实例,创建对象的动作叫做实例化,对象的属性叫做实例属性,对象调用的方法叫做实例方法

每个对象都有自己的独立的内存空间,保存不同的属性

三,封装的语法格式及其它细节

定义类:

class 类名():
    def 方法一(self, 参数列表):  # 一般为init初始化方法
        pass
    def 方法二(self, 参数列表):
        pass

 

创建对象:

对象的变量名 = 类名() 

如果类中定义了参数,就要传对应的参数

举例:

# 定义类

class Dog:
    kind = "狗"  # 类属性,所有对象的都是一样的

    def __init__(self, name, kind, age):  # 初始化方法,下面开始定义实例属性
        self.name = name  # 这三个都是实例属性
        self.kind = kind
        self.age = age

    def bark(self):  # 定义实例方法
        print("汪汪汪")


类的方法的self参数详解:

self其实就是类的对象,哪个对象调用了这个方法,self就是哪一个对象的应用。

eg:a调用了b类的方法,b类方法中的self就是a。

类属性和类方法

1,概念

py中可以说一切皆对象:class A: 定义的A 类为类对象, a=A():   a为实例对象

在程序运行时,类也会被加载到内存中,类是一个特殊的对象,即类对象,

 

类属性:给类对象中定义的属性,通常用来记录这个类的相关特征,定义的时候在class语句下方,直接使用赋值语句即可,可以通过类名. 或者在方法内部用self. 调用类属性

class Tool:
    count = 0  # 定义类属性count

    def __init__(self, name):
        self.name = name
        Tool.count += 1  # 针对类属性做一个计数 +1

 

类方法:针对类对象定义的方法,需要用@classmethod修饰,告诉解释器这是一个类方法。通过类名.调用类方法时不需要传递cls参数,在方法内部可通过cls.访问类属性,和类方法。其中cls代表类本身!的意义跟self一致。用的比较少

不可以用类访问实例属性,因为都还没有实例,就不存在所谓的实例属性!

语法格式:

class Tool:
    count = 0  # 定义类属性

    def __init__(self, name):
        self.name = name
        Tool.count += 1  # 针对类属性做一个计数 +1
        
    @classmethod
    def 类方法名(cls):
        pass

举例:

class Tool:
    count = 0  # 定义类属性

    @classmethod
    def show_tool_count(cls):
        print("工具对象的数量为:{}".format(cls.count))

    def __init__(self, name):
        self.name = name
        Tool.count += 1  # 针对类属性做一个计数 +1

静态方法

在开发过程中,需要在类中封装一个方法,不需要访问实例属性,调用实例方法,也不需要访问类属性和类方法,就可以封装成一个静态方法,其实就是一个函数放在了类的内部,方便管理,可以通过类名. 的方式调用静态方法。通过类名.调用静态方法,不需要创建对象。  静态方法实际上也用得不多。

需要用@staticmethod修饰

语法格式:

class Tool:
    count = 0  # 定义类属性

    @classmethod
    def show_tool_count(cls):
        print("工具对象的数量为:{}".format(cls.count))

    @staticmethod
    def tool_function():  # 定义一个静态方法,通过类名.调用静态方法
        print("锤子的作用是锤人!")

    def __init__(self, name):
        self.name = name
        Tool.count += 1  # 针对类属性做一个计数 +1

实例属性和实例方法

怎样定义实例属性?在init初始化方法内部,使用self.变量名 = 值的方式定义实例属性

实例方法--    在类内部定义的函数方法,即实例方法,无需任何装饰器装饰。

如下:

class Dog:
    kind = "狗"  # 类属性,所有对象的都是一样的

    def __init__(self, name, kind, age):  # 初始化方法,下面开始定义实例属性
        self.name = name  # 这三个都是实例属性
        self.kind = kind
        self.age = age

    def bark(self):  # 定义实例方法
        print("汪汪汪")

 

 

__init__ 方法详解

__init__方法,叫初始化方法,在对象被初始化时会自动调用。专门用来定义一个类具有哪些属性的方法,定义属性后,在使用这个类创建的对象,都会有该属性

初始化的同时,设置初始值

1:把希望设置的属性值,定义为__init__方法的参数(形参)

2:在方法内部使用self.属性 = 形参,的方式来接受外部传递的参数

3:创建对象时,使用类名(属性1,属性2,,,调用)

4;在定义属性时,如果不知道设置什么初始值,则设置为None

5:在实例化的同时,会自动调用__init__。初始化方法中有几个参数,在实例化的时候,就必须要传几个(类似函数的传参的操作)

 

如下图:

 

再者,一个对象(a)的属性可以时另一个类创建的对象(b),这样,a可以调用b中封装好的方法

 

小知识点:

is 是用来判断两个标识符是不是引用同一个对象,比较两个标识符的内存地址

x is y  可以看做, id(x) == id(y)

== 是用来判断引用变量的值是否相等。

在比较None的时候,要用is

四,类的继承,私有属性和私有方法

1,继承

继承就是子类拥有父类的所有方法和属性,可直接使用,无需再次开发,减少代码量,子类中还可以根据子类的实际情况,封装子类特有的属性和方法

2,语法格式

class 类名(父类名):

    pass

相关术语:字类又叫派生类,父类又叫基类,继承又叫派生

3,继承的特性,

传递性,字类拥有父类以及父类中封装的所有属性和方法

eg:c类继承b类,b类继承a类,则c类拥有b类,a类的所有属性和方法。

4,重写

当父类封装的方法不能满足子类的需求时,可对父类的方法进行重写(override)

1,完全覆盖

在字类中定义一个与父类方法同名的方法,并实现重写,在运行时,只会调用子类中重写的方法,不会调用父类方法。

2,扩展

父类方法不能完全满足子类需求,在子类中定义一个同名方法,里面包含着父类封装好的方法。就是扩展(可以用继承去理解)

步骤:

1,首先在子类中定义一个与父类方法同名的方法

2,使用super().父类方法,来调用父类中封装好的方法(切记,super必须在子类重写父类方法时才使用,即子类方法与父类方法同名的情况下)

3,在代码其它位置针对字类的需求,编写子类特有方法的代码实现。

注意事项:super实际是一个特殊的类,super()就是使用super类创建出来的对象,切记,不可用子类名.方法,去调用父类方法,会出现递归调用,死循环。

eg:

# 父类
class Base:
    def __init__(self, name):
        self.name = name

    def eat(self):
        print("吃")

    def sleep(self):
        print("睡觉")


class Cat(Base):  # 继承Base类
    def __init__(self, name, sex):  # 重写初始化方法,需要加上新的属性sex,但是又需要保持name属性
        super().__init__(name)  # 为了保持name属性,需要使用super来调用父类定义的__init__方法
        self.sex = sex  # 定义新的sex属性

    def climb(self):
        print("cat会爬树")
        self.eat()

    def sleep(self):
        # print("呼呼大睡")  # 完全覆盖,完全不使用父类
        super().sleep()  # 使用super调用父类方法,在下面扩展子类方法
        print("使用父类睡方法,又加上了一个睡觉的姿势")


c = Cat("英短","母")
c.sleep()

5,多继承

简单的说就是一个类可以有n个父类

语法: class 类名(父类1,父类2,父类3....)

会有一个问题,菱形继承,(可扩展,这里不做详解,不是重点内容)

继承顺序,自己有用自己的,没有就一层层往上找,最后找不到就报错。

菱形继承的时候,如右图: 

这个时候是广度优先

class A:
    def run(self):
        print("a is running")


class B(A):
    pass


class C(A):
    def run(self):
        print("c is  running")
        

class D(B, C):
    pass

D().run()  # 这种就是菱形继承,d先去找b类(为什么先找b类呢?因为从上到下的执行顺序),b类没有去找c类。 广度优先! 所以打印出来的是c is running

 

还有一种典型的情况,叫做v形继承:

class A:
    def run(self):
        print("a is running")


class B(A):
    pass


class C():
    def run(self):
        print("c is  running")


class D(B, C):
    pass

D().run()  # 这个时候是v形继承,深度优先,所以先找b,b没找到找a,所以是 a is running

 

print(D.__mro__) # 可以调用这个魔法函数,去打印出来,多继承中,继承查找的顺序

所有类都有一个祖先类-- object  python3中默认继承!!

五:动态属性设置

 

1,hasatter--  类定义后,用来判断一个类/对象是否含有一个属性,返回布尔值,True,False

eg:

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

    def eat(self):
        print("吃饭")


P = People("HAHA")
b = hasattr(P, "name")
print(b)  # 结果为True

2,getatter--  用于返回一个对象的属性值

语法: getattr(object,name[, defult])

object--对象

name-- 字符串,对象的属性

default -- 默认返回值,如果不写默认值,类中没有对应的属性时则会触发AttributeError 

返回值是对象的属性值

eg:

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

    def eat(self):
        print("吃饭")


a = People("HAHA")
aa = getattr(a, "name")
# aa = getattr(a, "name1")  # 此时不给默认值则报错
# aa = getattr(a, "name1", 22)  # 对象没有该属性,但是给了默认值,所以返回默认值不报错
print(aa)

3, setattr, 动态修改属性值,如果属性存在则修改值,如果不存在,则新增一个属性并且赋值

注意事项:如果是修改实例,或者新增实例属性,那么这个属性就只有对应实例能使用!!

语法:

setattr(object, name, value)

参数

  • object -- 对象。
  • name -- 字符串,对象属性。
  • value -- 属性值。

无返回值

eg:

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

    def eat(self):
        print("吃饭")


a = People("HAHA")
b = People("zhixiao")
setattr(a, "hight", 170)  # 给对象a设置新属性hight
setattr(People, "age", 18)  # 给people类设置新属性,age
# 因为age属性是给People类中新增的,所以,只要是People类的对象都有该属性
print(b.age)
print(a.age)

# 属性hight是专门给对象a设置的,所以只有对象a可以使用,当对象b使用时则会报属性不存在的错误
print(a.hight)
print(b.hight)

输出如下:

4,delattr,用来删除对象的属性,属性一定要存在,如果不存在则会报错

语法:

delattr(object, name)

参数

  • object -- 对象。
  • name -- 必须是对象的属性。
  • 无返回值  

eg:

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

    def eat(self):
        print("吃饭")


a = People("HAHA")
delattr(a, "name")  # 删除对象a中的name属性


print(a.name)  # 运行结果如下图

 

 

 

注意事项!!!

类中方法的参数,可以是其它类的实例对象!

eg:

class People:
    def eat(self):
        print("吃吃吃")


class Student:
    identity = "学生"

    def __init__(self, name, age, sex, e_sc, m_sc, c_sc):
        self.name = name
        self.age = age
        self.sex = sex
        self.e_sc = e_sc
        self.m_sc = m_sc
        self.c_sc = c_sc

    def get_all_sc(self):
        return self.e_sc + self.m_sc + self.c_sc

    def get_avg_sc(self):
        return self.get_all_sc() / 3

    def get_info(self):
        print("我的名字叫:{},年龄为: {},性别为:{}".format(self.name, self.age, self.sex))

    def who_eat(self, obj):
        # obj是另外一个类的对象,而且这个对象要具备eat方法,如果要使用该方法,则要先实例一个这个类的对象,否则无法调用!(函数的参数可以是任何类型,类也是一种数据类型)
        obj.eat()  # 调用其他类对象的方法,此处为调用People对象上的,obj是这里随便定义的


zxz = Student("小猪", 18, "男", 120, 100, 108)
zym = People()
zxz.who_eat(zym)

总结:在接口和web自动化框架中都会有运用到动态设置,修改或者删除类属性,特别是接口自动化中运用的比较多,要重点掌握

 

六: 仅做了解的知识点

1:重载,在java中有一个重载的概念(可自行扩展,这里不做详细介绍),主要是为了解决两个问题,

第一,一个类中的参数类型不一样, 第二:一个类中的参数个数不一样

但是对于python来说,参数类型不一样这个问题,因为python没用规定必须传同类型的参数,而是什么类型的参数都可以传,所以天然不存在这个问题。 对于参数个数来说,python,支持用*args,**kwargs,进行不定长传参!

 

2:多态

同一个方法在不同的类的对象中最终呈现出不同的效果,即为多态。因为python中函数传参不限定参数的类型,所以可以说python中一切皆多态。

eg:

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

    def whoAmI(self):
        return 'I am a Person, my name is %s' % self.name

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

    def whoAmI(self):
        return 'I am a Student, my name is %s' % self.name

class Teacher(Person):
    def __init__(self, name, gender, course):
        super(Teacher, self).__init__(name, gender)
        self.course = course

    def whoAmI(self):
        return 'I am a Teacher, my name is %s' % self.name

'''
在一个函数中,如果我们接收一个变量 x,则无论该 x 是 Person、Student还是 Teacher,都可以正确打印出结果,因为python中函数传参不限定参数的类型
'''

def who_am_i(x):
    print(x.whoAmI())

p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')

who_am_i(p)
who_am_i(s)
who_am_i(t)

 
# 运行结果如下图

 

都是同一个方法,def WhoAmI(self): 但是在不同的类的对象中,效果是不一样的,就是多态

3,私有属性和私有方法

在开发过程中,对象的某些属性和方法,只希望在对象的内部使用,不希望在外部被访问到,这时就可以设置为私有属性或方法

定义方式:

定义属性和方法时在属性名和方法名前加两个下划线__,就是定义为私有属性和私有方法,不能在外部直接调用

eg:

class woman:

    def __init__(self, name):
        self.name = name
        self.__age = 18  # 为私有属性,不能在外部被直接调用

    def secret(self):  # 在对象的方法内部,是可以访问对象的私有属性的,然后外部可以调用这个方法,就能访问私有属性了
        print("{}的年龄是:{}".format(self.name, self.__age))   #  除非这个方法也被定义成了私有方法就不能调用了

#  定义私有方法 def __secret(self)  这个方法就是私有方法
gg = woman("zhu")
gg.secret()

 

伪私有?

伪私有:py中,定义私有属性时,只是在属性名称和方法前加上了: __类名
所以想在外界访问私有属性时:只需要zhu._woman__secret,就可访问

 

当父类有不想被子类继承的方法或者属性时,就可以将其定义为私有属性和私有方法,私有化后,子类就不能继承私有属性了

eg:

class People:
    def __init__(self,money):
        self.__money = money

    def __speak(self):  # 私有化方法,不可以在外部使用,只能在类的内部被调用
        print("hello")

    def eat(self):
        print("吃吃吃")

class Man(People):
    def run(self):
        print("跑起来")

child  = People("100")
print(child.__money)    #父类中money为私有化属性,访问会报错
child.eat()
child.__speak()

man = Man(500)
man.__speak()

 

定义时;以一个下划线"_"开头的属性、方法,父类每部、子类内部均可以 使用,对象不可用

但是不是强制不可使用,只是一个使用规范,看到这个方法,应该知道不能去用。

 

在工作中,只有在源码中比较常见私有化,只做了解即可!!

 

后续踩坑,会持续更新~~

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值