python 面向对象

类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。

对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。

类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。

实例变量:定义在方法中的变量,只作用于当前实例的类。

对“类”和“对象”的使用:

  • 类就是一个模板,模板里可以包含多个函数,函数里实现一些功能。
  • 对象则是根据模板创建的实例,通过实例对象可以执行类中的函数。
#创建类
class Foo:
    # 类中的函数
    def bar(self):
        #功能阐述
        pass
# =====完毕========

#根据Foo创建对象obj
obj = Foo()
#创建对象的时候 记得后面加个括号

注意,按照Python通用规则,Class用驼峰式表示(HelloWorld)

而其他的obj等等,都用‘_’隔开(this_is_object)

类中的函数第一个参数必须是self,类中定义的函数叫做“方法”。

 创建类
class Foo:
     
    def bar(self):
        print('Bar')
 
    def hello(self, name):
        print('i am %s' %name)
 
# 根据Foo创建的对象
obj = Foo()
obj.Bar()            #执行Bar方法
obj.Hello('july') #执行Hello方法 

Bar
i am july

self 是个什么鬼呢?它是为了指代它所存在的类Class之中。

比如我们如果有好几个不同的obj被创建成同一个类,

那么有了self,我们的class Foo就能很好的知道哪个指的是自己,不会乱

# 创建类
class Foo:
    # 这里我们可以创建一个类级别的变量
    # 它不会随着由此类创建的变量而变化
    name = 'Jan'
    
    def bar(self):
        print('Bar')
 
    def hello(self, name):
        print('you are %s' %self.name)
        print('I am %s' %name)
        print('\n')
 
# 根据Foo创建的对象
obj1 = Foo()
obj2 = Foo()
obj1.hello('August')
obj2.hello('July') 

you are Jan
I am August

you are Jan
I am July

所以说,这个 self 就是个代指。代指了自己所在的class。你可以由 self 点进所指class本身的函数。由此可见,self 本身作为一个代词,并不一定要叫self。你也可以用个其他什么来代替。只不过,必须得是这个类的所有子方法的第一个参数:

# 创建类
class Foo:
    # 这里我们可以创建一个类级别的变量
    # 它不会随着由此类创建的变量而变化
    name = 'Jan'
    
    def bar(july):
        print('Bar')
 
    def hello(july, name): # 我这里把self改成了july,
        # 但是只要它作为第一参数的位置没变,它依旧是Foo Class的自我指代
        print('you are %s' %july.name)
        print('I am %s' %name)
        print('\n')
 
# 根据Foo创建的对象
obj1 = Foo()
obj2 = Foo()
obj1.hello('August')
obj2.hello('July') 

you are Jan
I am August

you are Jan
I am July

构造函数:构造函数,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值。

跟所有OOP语言一样,python也是有构造函数的,默认为:

# 创建类
class Foo:
    
    def __init__(self):#这就是构造函数,它的职责是在模型创建的初期,就完成一些动作
        #简单的说就是,自定义的初始化步骤:
        #同样,它需要self来指代本身这个class
        self.name='Jan'
 
    def hello(self, name):
        print('you are %s' %self.name)
        print('I am %s' %name)
        print('\n')
# ==== 完毕 =====

#当你创建一个Foo类的时候,init会被自动跑一遍:
obj = Foo()
# 在我们的例子中,我们默认给self自己的name变量,赋值为’JAN‘
# 此刻,当我们调用Foo的hello()方法时,hello自己的name变量,被赋值为'July'
obj.hello('July')

you are Jan
I am July

init是可以带更多的参数的,用以初始化我们的class本身。

比如说,你要初始化一个类的时候要用到一些外部参数:

# 创建类
class Foo:
    
    def __init__(self, name2):# 你可以在这里附加上一些参数
        # 这些参数将是你创建一个Foo类时的必要条件
        self.name=name2
 
    def hello(self, name):
        print('you are %s' %self.name)
        print('I am %s' %name)
        print('\n')
# ==== 完毕 =====

#当你创建一个Foo类的时候,init会被自动跑一遍:
# 此刻,你不可以直接跑Foo(),你需要填入一个参数:name2
obj = Foo('Feb')
# 然后再调用hello, 并赋值July
obj.hello('July')

you are Feb
I am July

由楼上这些例子,我们大概可以知道整个Python的OOP概念了:

Class(类)就是一个把一堆Object(对象?)集合起来的地方。

在这里,无论是变量还是方法,他们享有基本一样的层级概念。只不过,方法要做一点事儿,而变量直接就是一个值。

 

访问限制

我们刚刚看到,在调用obj的时候,可以直接调出name或者使用hello()。那么我们怎么知道什么时候可以调用他们,什么时候不可以呢?

在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问

举个学生的例子,我们可以用一个学生类来存储学生的信息,但是我们在外部可以接触到name,那么其实我们就是可以直接修改name的,这是不安全的

class Student:
    
    def __init__(self, name, age):
        self.name = name
        self.age = age  
    
    def detail(self):
        print(self.name)
        print(self.age)

LiLei = Student('LiLei', 12)
LiLei.age = 20
LiLei.detail()

LiLei
20

为了防止这种篡改年龄的事情发生,为了维护世界的和平,我们需要把关键的信息给做好隐藏:

class Student:
    
    def __init__(self, name, age):
        self.__name = name
        self.__age = age  
    
    def detail(self):
        print(self.__name)
        print(self.__age)

LiLei = Student('LiLei', 12)
LiLei.__age = 20
LiLei.detail()

LiLei
12

看,如此一来,年龄就不会被更改了。

那么如何既保证安全,又能被外部修改呢?

我们使用OOP家族传统理念:Getter+Setter

class Student(object):
    ...

    def get_name(self):
        return self.__name

    def get_age(self):
        return self.__age
class Student(object):
    ...

    def set_age(self, score):
        self.__age = age

至此,我们应该学会使用Class来定义我们自己的类了

接下来,我们来看看:

 

在Python中展现面向对象三大特性:

面向对象的三大特性是指:封装、继承和多态。

封装

指的就是把内容封装到某个地方,用于日后调用

它需要:

  • 把内容封装在某处

  • 从另一处调用被封装的内容

通过对象直接调用

我们可以在存完一个内容以后,在类以外的地方,通过这个类的对象,来直接”点“调用

class Student:
    # 假定我们初始化一个Student类的时候要做的就是,记录下每个学生的名字和年龄
    def __init__(self, name, age):
        self.name = name
        self.age = age  
    # 至此,我们用self指代student本身,并用name和age存下了他们的年龄和名字

    # === 完毕 ===

#此时,我们新建一个学生
obj1 = Student('July', 18)
print(obj1.name)    # 直接调用obj1对象的name属性
print(obj1.age)   # 直接调用obj1对象的age属性
obj2 = Student('Aug', 73)
print(obj2.name)    # 直接调用obj2对象的name属性
print(obj2.age)     # 直接调用obj2对象的age属性

July
18
Aug
73

通过self间接调用

执行类中某一个方法时,通过self来调用了类自己的变量

class Student:
    
    def __init__(self, name, age):
        self.name = name
        self.age = age  
    
    def detail(self):
        print(self.name)
        print(self.age)

    # === 完毕 ===

#此时,我们新建一个学生
obj1 = Student('July', 18)
obj1.detail() #Python默认将obj1传给self,所以其实我们做的是obj1.detail(obj1)
# 那么,detail()内部的样貌其实就是:
# print(obj1.name)
# print(obj1.age)
obj2 = Student('Aug', 73)
obj2.detail()

July
18
Aug
73

综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。

 

继承

继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容(爸爸有的儿子都有)。

例如,每个学生都有名字和年龄,木有问题。我们可以把这个作为我们的父亲类。

但是,每个学生自己,可能有自己不同的”方法“,比如,每个人有每个人不同的外号,不同的口号,不同的饮食习惯,不同的。

# 我们首先创建一个学生类,这个类是所有学生的爸爸
class Student:
    
    def __init__(self, name, age):
        self.name = name
        self.age = age  
    
    def detail(self):
        print(self.name)
        print(self.age)

# 然后,我们创建一个小学生类,小学生特点是,LOL sala无敌
class PrimaryStudent(Student):#因为是继承于学生类,所以我们写在括号内
    # 这里我们可以不写构造函数,于是我们就是直接沿用Student类的构造函数
    def lol(self): # 我们有一些新的独有的方法,会被叠加起来
        print('不服sala!')
    
# 接下来,我们创建一个大学生类,大学生特点是,额,每个人都有个妹子。。
class CollegeStudent(Student):
    def __init__(self, name, age, gf): #这里,我们改写一下构造函数
        # 于是爸爸的init会被直接overwrite
        self.name = name
        self.age = age
        self.gf = gf
    def gf_detail(self):
        print(self.gf)

# 来,我们来创建一下
obj1 = PrimaryStudent('小王', 7)
obj1.lol() # 独有的方法
obj1.detail()#继承与爸爸的方法

obj2 = CollegeStudent('王王', 29, '张张')
obj2.detail()
obj2.gf_detail()

不服sala!
小王
7
王王
29
张张

所以,对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。

这样可以极大的提高效率,减少代码的重复。

 

问题来了,如果我想多认个干爹呢?

Python和Java/C#的不同就是,Python可以多类继承,也就是,可以认很多干爹

但是干爹多了,就出了问题了。继承的时候,从谁先开始?

有两种方式,分别是深度优先和广度优先

  • 当本身的类是经典类的时候,就按照深度优先方式查找继承的方法 (即,找到一个爸爸,继续找这个爸爸的爸爸,爸爸的爸爸的爸爸。。。)

  • 当本身的类是新式类的时候,就按照广度优先的方式查找 (即,找到一个爸爸,再找下一个爸爸,再找下一个爸爸,平辈之间查找)

那么为什么有经典类新类之分呢?

这是个历史遗留问题,新类 统一了类(class)和类型(type),所以其实也是社区推荐的写法,只不过。。很多程序员都很懒。。

在2.2之前,比如2.1版本中,类和类型是不同的,如a是ClassA的一个实例,那么a.__class__返回 ‘ class    __main__.ClassA‘ ,type(a)返回总是<type 'instance'>。而引入新类后,比如ClassB是个新类,b是ClassB的实例,b.__class__和type(b)都是返回‘class '__main__.ClassB' ,这样就统一了。

于是乎,在新版的Python中,这个经典类和新类的区别已经不存在,都统一使用广度优先。

我们先假设我们还活在python2.2的时代:

#经典类的写法
class c1:
    pass
class c2(c1):
    pass

#新类的写法
class N1(object):
    pass
class N2(N1):
    pass

可见,新类的标志就是,大家的老祖宗继承于一个系统级的类,叫Object

具体的,我们来看看:

  • 经典类
class D:

    def bar(self):
        print('D.bar')


class C(D):

    def bar(self):
        print('C.bar')


class B(D):

    pass


class A(B, C):

    pass

a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> D --> C
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
a.bar()

C.bar
  • 新类
class D(object):

    def bar(self):
        print('D.bar')


class C(D):

    def bar(self):
        print('C.bar')


class B(D):

    pass

class A(B, C):

    pass

a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> C --> D
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
a.bar()

C.bar

当然,对我们先现在而言,两种写法都得出C.bar ;这说明,已经木有区别了。

 

多态

Pyhon不支持多态并且也用不到多态,多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型(Duck Typing)”。

什么是鸭子类型?其实翻译成中文最好是叫:好猫类型。

也就是引用了小平同志的一句话,不管黑猫白猫抓到老鼠的就是好猫。

不同于强类型的语言,一个类型的obj只能一种事儿,

在Python中,只要是能“不报错运行”的类型,都可以塞进参数中去:

class F1:
    pass

# 假设,S1是我们的正统类,它继承于根正苗红的F1,是我们的正统类
class S1(F1):
    def show(self):
        print('S1.show')

# S2是路人甲,是个歪瓜裂枣,但是他自己也有一个叫show的方法。
class S2:
    def show(self):
        print('S2.show')
        
        
# 在Java或C#中定义函数参数时,必须指定参数的类型,也即是说,我们如果用
# Java写下面的Func,需要告知,obj是F1类还是其他什么东西。
# 如果限定了F1,那么S2是不可以被采纳的。
# 然而,在Python中,一切都是Obj,它不care你到底是什么类,直接塞进去就可以

def Func(obj):
    """Func函数需要接收一个F1类型或者F1子类的类型"""
    obj.show()
    
s1_obj = S1()
Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show

s2_obj = S2()
Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show

S1.show
S2.show

 

实战

我们现在已经完全掌握了使用包,自己定义类,组件一个可运行的程序的方法

现在我们可以专注于Machine Learning方面,来看看实战是怎么运用这些知识的。

from sklearn import svm, datasets

class Dataset:
    # 我们创造一个dataset的类,这个类会帮我们下载相关的数据集,
    # 并给我们分类好x,y
    def __init__(self, name):
        # 告诉类,我们需要哪一个数据集
        # 我们有两个选择,一个是'iris'一个是'digits'
        self.name = name
        
    def download_data(self):
        # 从sklearn的自带集中下载我们指定的数据集
        if self.name == 'iris':
            # 这里是sklearn自带的数据集下载方法,更多信息可以参照官网
            self.downloaded_data = datasets.load_iris()
        elif self.name == 'digits':
            self.downloaded_data = datasets.load_digits()
        else:
            # 如果不是我们预想的两种数据集,则报错
            print('Dataset Error: No named datasets')
    
    def generate_xy(self):
        # 通过这个过程来把我们的数据集分为原始数据以及他们的label
        # 我们先把数据下载下来
        self.download_data()
        x = self.downloaded_data.data
        y = self.downloaded_data.target
        print('\nOriginal data looks like this: \n', x)
        print('\nLabels looks like this: \n', y)
        return x,y
    
    def get_train_test_set(self, ratio):
        # 这里,我们把所有的数据分成训练集和测试集
        # 一个参数要求我们告知,我们以多少的比例来分割训练和测试集
        # 首先,我们把XY给generate出来:
        x, y = self.generate_xy()
        
        # 有个比例,我们首先得知道 一共有多少的数据
        n_samples = len(x)
        # 于是我们知道,有多少应该是训练集,多少应该是测试集
        n_train = n_samples * ratio
        # 好了,接下来我们分割数据
        X_train = x[:n_train]
        y_train = y[:n_train]
        X_test = x[n_train:]
        y_test = y[n_train:]
        # 好,我们得到了所有想要的玩意儿
        return X_train, y_train, X_test, y_test
# ====== 我们的dataset类创造完毕=======

接下来,我们在main中code以下来调用我们自己写的类:

# 比如,我们使用digits数据集
data = Dataset('digits')
# 接着,我们可以用0.7的分割率把xy给分割出来
X_train, y_train, X_test, y_test = data.get_train_test_set(0.7)
Original data looks like this: 
 [[  0.   0.   5. ...,   0.   0.   0.]
 [  0.   0.   0. ...,  10.   0.   0.]
 [  0.   0.   0. ...,  16.   9.   0.]
 ..., 
 [  0.   0.   1. ...,   6.   0.   0.]
 [  0.   0.   2. ...,  12.   0.   0.]
 [  0.   0.  10. ...,  12.   1.   0.]]

Labels looks like this: 
 [0 1 2 ..., 8 9 8]

同样,我们也不一定需要自己创造类,我们可以引用第三方库里的类, 比如这里,我们用SVM作为我们的分类器,去训练我们的算法 我们就直接建造一个object,使他成为SVM类

clf = svm.SVC()

这里 clf 是 classifier的简称,SVC指的是SVM的classification版本。

因为我们的数据集都是分类问题,所以我们使用SVC()

接下来,我们fit我们的数据(也就是训练我们的数据)

显然,做fit的时候,我们只可以使用训练集

clf.fit(X_train, y_train)

SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape=None, degree=3, gamma='auto', kernel='rbf',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False)

好,现在你的clf已经训练好了,我们来看看它的表现:

我们随便取test集中的一个数据点,并对应它的真实label

test_point = X_test[12]
y_true = y_test[12]

我们来看看,我们的clf给出的预测是什么:

clf.predict(test_point)

array([7])

再看看真正的label应该是什么:

y_true

7

正确!

那么这样,你们已经学会如何训练数据集并作出新的预测了。

把所有的测试集都导入clf,让他pridict,并看看跟真实的label相差多少。

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python面向对象编程是一种编程范式,它将程序组织成对象的集合,每个对象都有自己的属性和方法。在Python,可以通过定义类来创建对象,并通过实例化类来创建具体的对象。引用[1]的代码示例展示了一个Animal类,其包含了初始化方法和一个__str__方法来返回对象的描述信息。通过这个类,可以创建Animal对象,并通过print函数输出对象。引用的代码示例展示了如何使用@property装饰器来定义类的属性和属性的访问方法。通过这种方式,可以在访问属性时像访问普通属性一样使用点号,而不需要使用方法调用的方式。引用的代码示例展示了多态在Python的应用。多态是面向对象编程的重要概念,它允许不同的对象以相同的方式对外部访问,但具体的实现可能不同。在这个示例,father、son和daughter类都继承了father类,并重写了tell方法。通过调用不同的对象的tell方法,可以看到不同的输出结果。总之,Python面向对象编程是一种灵活且强大的编程方式,它允许开发者以对象为心来思考和组织代码,提高了代码的可读性和可维护性。通过定义类、创建对象和使用类的属性和方法,可以实现丰富多样的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Python面向对象(全套)](https://blog.csdn.net/Thewei666/article/details/126652501)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值