Python语言进阶

Python语言进阶

数据结构和算法

  • 算法:解决问题的方法和步骤

  • 评价算法的好坏:渐近时间复杂度和渐近空间复杂度。

  • 渐近时间复杂度的大O标记:

image.png
image.png
  • 排序算法(选择、冒泡和归并)和查找算法(顺序和折半)

    def select_sort(origin_items, comp=lambda x, y: x < y):
        """简单选择排序"""
        items = origin_items[:]
        for i in range(len(items) - 1):
            min_index = i
            for j in range(i + 1, len(items)):
                if comp(items[j], items[min_index]):
                    min_index = j
            items[i], items[min_index] = items[min_index], items[i]
        return items
    
    def bubble_sort(origin_items, comp=lambda x, y: x > y):
        """高质量冒泡排序(搅拌排序)"""
        items = origin_items[:]
        for i in range(len(items) - 1):
            swapped = False
            for j in range(i, len(items) - 1 - i):
                if comp(items[j], items[j + 1]):
                    items[j], items[j + 1] = items[j + 1], items[j]
                    swapped = True
            if swapped:
                swapped = False
                for j in range(len(items) - 2 - i, i, -1):
                    if comp(items[j - 1], items[j]):
                        items[j], items[j - 1] = items[j - 1], items[j]
                        swapped = True
            if not swapped:
                break
        return items
    
    def merge_sort(items, comp=lambda x, y: x <= y):
        """归并排序(分治法)"""
        if len(items) < 2:
            return items[:]
        mid = len(items) // 2
        left = merge_sort(items[:mid], comp)
        right = merge_sort(items[mid:], comp)
        return merge(left, right, comp)
    
    
    def merge(items1, items2, comp):
        """合并(将两个有序的列表合并成一个有序的列表)"""
        items = []
        index1, index2 = 0, 0
        while index1 < len(items1) and index2 < len(items2):
            if comp(items1[index1], items2[index2]):
                items.append(items1[index1])
                index1 += 1
            else:
                items.append(items2[index2])
                index2 += 1
        items += items1[index1:]
        items += items2[index2:]
        return items
    
    def seq_search(items, key):
        """顺序查找"""
        for index, item in enumerate(items):
            if item == key:
                return index
        return -1
    
    def bin_search(items, key):
        """折半查找"""
        start, end = 0, len(items) - 1
        while start <= end:
            mid = (start + end) // 2
            if key > items[mid]:
                start = mid + 1
            elif key < items[mid]:
                end = mid - 1
            else:
                return mid
        return -1
    
  • 使用生成式(推导式)语法

    prices = {
        'AAPL': 191.88,
        'GOOG': 1186.96,
        'IBM': 149.24,
        'ORCL': 48.44,
        'ACN': 166.89,
        'FB': 208.09,
        'SYMC': 21.29
    }
    # 用股票价格大于100元的股票构造一个新的字典
    prices2 = {key: value for key, value in prices.items() if value > 100}
    print(prices2)
    

    说明:生成式(推导式)可以用来生成列表、集合和字典。

  • 嵌套的列表

    names = ['关羽', '张飞', '赵云', '马超', '黄忠']
    courses = ['语文', '数学', '英语']
    # 录入五个学生三门课程的成绩
    # 错误 - 参考http://pythontutor.com/visualize.html#mode=edit
    # scores = [[None] * len(courses)] * len(names)
    scores = [[None] * len(courses) for _ in range(len(names))]
    for row, name in enumerate(names):
        for col, course in enumerate(courses):
            scores[row][col] = float(input(f'请输入{name}的{course}成绩: '))
            print(scores)
    

    Python Tutor - VISUALIZE CODE AND GET LIVE HELP

  • heapq、itertools等的用法

    """
    从列表中找出最大的或最小的N个元素
    堆结构(大根堆/小根堆)
    """
    import heapq
    
    list1 = [34, 25, 12, 99, 87, 63, 58, 78, 88, 92]
    list2 = [
        {'name': 'IBM', 'shares': 100, 'price': 91.1},
        {'name': 'AAPL', 'shares': 50, 'price': 543.22},
        {'name': 'FB', 'shares': 200, 'price': 21.09},
        {'name': 'HPQ', 'shares': 35, 'price': 31.75},
        {'name': 'YHOO', 'shares': 45, 'price': 16.35},
        {'name': 'ACME', 'shares': 75, 'price': 115.65}
    ]
    print(heapq.nlargest(3, list1))
    print(heapq.nsmallest(3, list1))
    print(heapq.nlargest(2, list2, key=lambda x: x['price']))
    print(heapq.nlargest(2, list2, key=lambda x: x['shares']))
    
    """
    迭代工具 - 排列 / 组合 / 笛卡尔积
    """
    import itertools
    
    itertools.permutations('ABCD')
    itertools.combinations('ABCDE', 3)
    itertools.product('ABCD', '123')
    
  • collections模块下的工具类

    """
    找出序列中出现次数最多的元素
    """
    from collections import Counter
    
    words = [
        'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes',
        'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around',
        'the', 'eyes', "don't", 'look', 'around', 'the', 'eyes',
        'look', 'into', 'my', 'eyes', "you're", 'under'
    ]
    counter = Counter(words)
    print(counter.most_common(3))
    
  • 常用算法:

    • 穷举法 - 又称为暴力破解法,对所有的可能性进行验证,直到找到正确答案。
    • 贪婪法 - 在对问题求解时,总是做出在当前看来
    • 最好的选择,不追求最优解,快速找到满意解。
    • 分治法 - 把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题,直到可以直接求解的程度,最后将子问题的解进行合并得到原问题的解。
    • 回溯法 - 回溯法又称为试探法,按选优条件向前搜索,当搜索到某一步发现原先选择并不优或达不到目标时,就退回一步重新选择。
    • 动态规划 - 基本思想也是将待求解问题分解成若干个子问题,先求解并保存这些子问题的解,避免产生大量的重复运算。

    穷举法例子:百钱百鸡和五人分鱼。

    # 公鸡5元一只 母鸡3元一只 小鸡1元三只
    # 用100元买100只鸡 问公鸡/母鸡/小鸡各多少只
    for x in range(20):
        for y in range(33):
            z = 100 - x - y
            if 5 * x + 3 * y + z // 3 == 100 and z % 3 == 0:
                print(x, y, z)
    
    # A、B、C、D、E五人在某天夜里合伙捕鱼 最后疲惫不堪各自睡觉
    # 第二天A第一个醒来 他将鱼分为5份 扔掉多余的1条 拿走自己的一份
    # B第二个醒来 也将鱼分为5份 扔掉多余的1条 拿走自己的一份
    # 然后C、D、E依次醒来也按同样的方式分鱼 问他们至少捕了多少条鱼
    fish = 6
    while True:
        total = fish
        enough = True
        for _ in range(5):
            if (total - 1) % 5 == 0:
                total = (total - 1) // 5 * 4
            else:
                enough = False
                break
        if enough:
            print(fish)
            break
        fish += 5
    

    贪婪法例子:假设小偷有一个背包,最多能装20公斤赃物,他闯入一户人家,发现如下表所示的物品。很显然,他不能把所有物品都装进背包,所以必须确定拿走哪些物品,留下哪些物品。

    名称 价格(美元) 重量(kg)
    电脑 200 20
    收音机 20 4
    175 10
    花瓶 50 2
    10 1
    油画 90 9
    """
    贪婪法:在对问题求解时,总是做出在当前看来是最好的选择,不追求最优解,快速找到满意解。
    输入:
    20 6
    电脑 200 20
    收音机 20 4
    钟 175 10
    花瓶 50 2
    书 10 1
    油画 90 9
    """
    class Thing(object):
        """物品"""
    
        def __init__(self, name, price, weight):
            self.name = name
            self.price = price
            self.weight = weight
    
        @property
        def value(self):
            """价格重量比"""
            return self.price / self.weight
    
    
    def input_thing():
        """输入物品信息"""
        name_str, price_str, weight_str = input().split()
        return name_str, int(price_str), int(weight_str)
    
    
    def main():
        """主函数"""
        max_weight, num_of_things = map(int, input().split())
        all_things = []
        for _ in range(num_of_things):
            all_things.append(Thing(*input_thing()))
        all_things.sort(key=lambda x: x.value, reverse=True)
        total_weight = 0
        total_price = 0
        for thing in all_things:
            if total_weight + thing.weight <= max_weight:
                print(f'小偷拿走了{thing.name}')
                total_weight += thing.weight
                total_price += thing.price
        print(f'总价值: {total_price}美元')
    
    
    if __name__ == '__main__':
        main()
    

    分治法例子:快速排序。

    """
    快速排序 - 选择枢轴对元素进行划分,左边都比枢轴小右边都比枢轴大
    """
    def quick_sort(origin_items, comp=lambda x, y: x <= y):
        items = origin_items[:]
        _quick_sort(items, 0, len(items) - 1, comp)
        return items
    
    
    def _quick_sort(items, start, end, comp):
        if start < end:
            pos = _partition(items, start, end, comp)
            _quick_sort(items, start, pos - 1, comp)
            _quick_sort(items, pos + 1, end, comp)
    
    
    def _partition(items, start, end, comp):
        pivot = items[end]
        i = start - 1
        for j in range(start, end):
            if comp(items[j], pivot):
                i += 1
                items[i], items[j] = items[j], items[i]
        items[i + 1], items[end] = items[end], items[i + 1]
        return i + 1
    

    回溯法例子:骑士巡逻。

    """
    递归回溯法:叫称为试探法,按选优条件向前搜索,当搜索到某一步,发现原先选择并不优或达不到目标时,就退回一步重新选择,比较经典的问题包括骑士巡逻、八皇后和迷宫寻路等。
    """
    import sys
    import time
    
    SIZE = 5
    total = 0
    
    
    def print_board(board):
        for row in board:
            for col in row:
                print(str(col).center(4), end='')
            print()
    
    
    def patrol(board, row, col, step=1):
        if row >= 0 and row < SIZE and \
            col >= 0 and col < SIZE and \
            board[row][col] == 0:
            board[row][col] = step
            if step == SIZE * SIZE:
                global total
                total += 1
                print(f'第{total}种走法: ')
                print_board(board)
            patrol(board, row - 2, col - 1, step + 1)
            patrol(board, row - 1, col - 2, step + 1)
            patrol(board, row + 1, col - 2, step + 1)
            patrol(board, row + 2, col - 1, step + 1)
            patrol(board, row + 2, col + 1, step + 1)
            patrol(board, row + 1, col + 2, step + 1)
            patrol(board, row - 1, col + 2, step + 1)
            patrol(board, row - 2, col + 1, step + 1)
            board[row][col] = 0
    
    
    def main():
        board = [[0] * SIZE for _ in range(SIZE)]
        patrol(board, SIZE - 1, SIZE - 1)
    
    
    if __name__ == '__main__':
        main()
    

    动态规划例子1:斐波拉切数列。(不使用动态规划将会是几何级数复杂度)

    """
    动态规划 - 适用于有重叠子问题和最优子结构性质的问题
    使用动态规划方法所耗时间往往远少于朴素解法(用空间换取时间)
    """
    def fib(num, temp={}):
        """用递归计算Fibonacci数"""
        if num in (1, 2):
            return 1
        try:
            return temp[num]
        except KeyError:
            temp[num] = fib(num - 1) + fib(num - 2)
            return temp[num]
    

    动态规划例子2:子列表元素之和的最大值。(使用动态规划可以避免二重循环)

    说明:子列表指的是列表中索引(下标)连续的元素构成的列表;列表中的元素是int类型,可能包含正整数、0、负整数;程序输入列表中的元素,输出子列表元素求和的最大值,例如:

    输入:1 -2 3 5 -3 2

    输出:8

    输入:0 -2 3 5 -1 2

    输出:9

    输入:-9 -2 -3 -5 -3

    输出:-2

    def main():
        items = list(map(int, input().split()))
        size = len(items)
        overall, partial = {}, {}
        overall[size - 1] = partial[size - 1] = items[size - 1]
        for i in range(size - 2, -1, -1):
            partial[i] = max(items[i], partial[i + 1] + items[i])
            overall[i] = max(partial[i], overall[i + 1])
        print(overall[0])
    
    
    if __name__ == '__main__':
        main()
    

函数的使用方式

  • 将函数视为“一等公民”

    • 函数可以赋值给变量
    • 函数可以作为函数的参数
    • 函数可以作为函数的返回值
  • 高阶函数的用法(filtermap以及它们的替代品)

    items1 = list(map(lambda x: x ** 2, filter(lambda x: x % 2, range(1, 10))))
    items2 = [x ** 2 for x in range(1, 10) if x % 2]
    
  • 位置参数、可变参数、关键字参数、命名关键字参数

  • 参数的元信息(代码可读性问题)

  • 匿名函数和内联函数的用法(lambda函数)

  • 闭包和作用域问题

    • Python搜索变量的LEGB顺序(Local --> Embedded --> Global --> Built-in)

    • globalnonlocal关键字的作用

      global:声明或定义全局变量(要么直接使用现有的全局作用域的变量,要么定义一个变量放到全局作用域)。

      nonlocal:声明使用嵌套作用域的变量(嵌套作用域必须存在该变量,否则报错)。

  • 装饰器函数(使用装饰器和取消装饰器)

    例子:输出函数执行时间的装饰器。

    def record_time(func):
        """自定义装饰函数的装饰器"""
        
        @wraps(func)
        def wrapper(*args, **kwargs):
            start = time()
            result = func(*args, **kwargs)
            print(f'{func.__name__}: {time() - start}秒')
            return result
            
        return wrapper
    

    如果装饰器不希望跟print函数耦合,可以编写带参数的装饰器。

    from functools import wraps
    from time import time
    
    
    def record(output):
        """自定义带参数的装饰器"""
       
       def decorate(func):
           
           @wraps(func)
           def wrapper(*args, **kwargs):
               start = time()
               result = func(*args, **kwargs)
               output(func.__name__, time() - start)
               return result
                
           return wrapper
       
       return decorate
    
    from functools import wraps
    from time import time
    
    
    class Record():
        """自定义装饰器类(通过__call__魔术方法使得对象可以当成函数调用)"""
    
        def __init__(self, output):
            self.output = output
    
        def __call__(self, func):
    
            @wraps(func)
            def wrapper(*args, **kwargs):
                start = time()
                result = func(*args, **kwargs)
                self.output(func.__name__, time() - start)
                return result
    
            return wrapper
    

    说明:由于对带装饰功能的函数添加了@wraps装饰器,可以通过func.__wrapped__方式获得被装饰之前的函数或类来取消装饰器的作用。

    例子:用装饰器来实现单例模式。

    from functools import wraps
    
    
    def singleton(cls):
        """装饰类的装饰器"""
        instances = {}
    
        @wraps(cls)
        def wrapper(*args, **kwargs):
            if cls not in instances:
                instances[cls] = cls(*args, **kwargs)
            return instances[cls]
    
        return wrapper
    
    
    @singleton
    class President():
        """总统(单例类)"""
        pass
    

    说明:上面的代码中用到了闭包(closure),不知道你是否已经意识到了。还没有一个小问题就是,上面的代码并没有实现线程安全的单例,如果要实现线程安全的单例应该怎么做呢?

    from functools import wraps
    from threading import Lock
    
    
    def singleton(cls):
        """线程安全的单例装饰器"""
        instances = {}
        locker = Lock()
    
        @wraps(cls)
        def wrapper(*args, **kwargs):
            if cls not in instances:
                with locker:
                    if cls not in instances:
                        instances[cls] = cls(*args, **kwargs)
            return instances[cls]
    
        return wrapper
    

面向对象相关知识

  • 三大支柱:

    • 封装
    • 继承
    • 多态

    例子:工资结算系统。

    """
    月薪结算系统 - 部门经理每月15000 程序员每小时200 销售员1800底薪加销售额5%提成
    """
    from abc import ABCMeta, abstractmethod
    
    
    class Employee(metaclass=ABCMeta):
        """员工(抽象类)"""
    
        def __init__(self, name):
            self.name = name
    
        @abstractmethod
        def get_salary(self):
            """结算月薪(抽象方法)"""
            pass
    
    
    class Manager(Employee):
        """部门经理"""
    
        def get_salary(self):
            return 15000.0
    
    
    class Programmer(Employee):
        """程序员"""
    
        def __init__(self, name, working_hour=0):
            self.working_hour = working_hour
            super().__init__(name)
    
        def get_salary(self):
            return 200.0 * self.working_hour
    
    
    class Salesman(Employee):
        """销售员"""
    
        def __init__(self, name, sales=0.0):
            self.sales = sales
            super().__init__(name)
    
        def get_salary(self):
            return 1800.0 + self.sales * 0.05
    
    
    class EmployeeFactory():
        """创建员工的工厂(工厂模式 - 通过工厂实现对象使用者和对象之间的解耦合)"""
    
        @staticmethod
        def create(emp_type, *args, **kwargs):
            """创建员工"""
            emp_type = emp_type.upper()
            emp = None
            if emp_type == 'M':
                emp = Manager(*args, **kwargs)
            elif emp_type == 'P':
                emp = Programmer(*args, **kwargs)
            elif emp_type == 'S':
                emp = Salesman(*args, **kwargs)
            return emp
    
    
    def main():
        """主函数"""
        emps = [
            EmployeeFactory.create('M', '曹操'), 
            EmployeeFactory.create('P', '荀彧', 120),
            EmployeeFactory.create('P', '郭嘉', 85), 
            EmployeeFactory.create('S', '典韦', 123000),
        ]
        for emp in emps:
            print('%s: %.2f元' % (emp.name, emp.get_salary()))
    
    
    if __name__ == '__main__':
        main()
    
  • 类与类之间的关系

    • is-a关系:继承
    • has-a关系:关联 / 聚合 / 合成
    • use-a关系:依赖

    例子:扑克游戏。

    """
    经验:符号常量总是优于字面常量,枚举类型是定义符号常量的最佳选择
    """
    from enum import Enum, unique
    
    import random
    
    
    @unique
    class Suite(Enum):
        """花色"""
    
        SPADE, HEART, CLUB, DIAMOND = range(4)
    
        def __lt__(self, other):
            return self.value < other.value
    
    
    class Card():
        """牌"""
    
        def __init__(self, suite, face):
            """初始化方法"""
            self.suite = suite
            self.face = face
    
        def show(self):
            """显示牌面"""
            suites = ['♠️', '♥️', '♣️', '♦️']
            faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
            return f'{suites[self.suite.value]} {faces[self.face]}'
    
        def __str__(self):
            return self.show()
    
        def __repr__(self):
            return self.show()
    
    
    class Poker():
        """扑克"""
    
        def __init__(self):
            self.index = 0
            self.cards = [Card(suite, face)
                          for suite in Suite
                          for face in range(1, 14)]
    
        def shuffle(self):
            """洗牌(随机乱序)"""
            random.shuffle(self.cards)
            self.index = 0
    
        def deal(self):
            """发牌"""
            card = self.cards[self.index]
            self.index += 1
            return card
    
        @property
        def has_more(self):
            return self.index < len(self.cards)
    
    
    class Player():
        """玩家"""
    
        def __init__(self, name):
            self.name = name
            self.cards = []
    
        def get_one(self, card):
            """摸一张牌"""
            self.cards.append(card)
    
        def sort(self, comp=lambda card: (card.suite, card.face)):
            """整理手上的牌"""
            self.cards.sort(key=comp)
    
    
    def main():
        """主函数"""
        poker = Poker()
        poker.shuffle()
        players = [Player('东邪'), Player('西毒'), Player('南帝'), Player('北丐')]
        while poker.has_more:
            for player in players:
                    player.get_one(poker.deal())
        for player in players:
            player.sort()
            print(player.name, end=': ')
            print(player.cards)
    
    
    if __name__ == '__main__':
        main()
    

    说明:上面的代码中使用了Emoji字符来表示扑克牌的四种花色,在某些不支持Emoji字符的系统上可能无法显示。

  • 对象的复制(深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆)

  • 垃圾回收、循环引用和弱引用

    Python使用了自动化内存管理,这种管理机制以引用计数为基础,同时也引入了标记-清除分代收集两种机制为辅的策略。

    typedef struct_object {
        /* 引用计数 */
        int ob_refcnt;
        /* 对象指针 */
        struct_typeobject *ob_type;
    } PyObject;
    
    /* 增加引用计数的宏定义 */
    #define Py_INCREF(op)   ((op)->ob_refcnt++)
    /* 减少引用计数的宏定义 */
    #define Py_DECREF(op) \ //减少计数
        if (--(op)->ob_refcnt != 0) \
            ; \
        else \
            __Py_Dealloc((PyObject *)(op))
    

    导致引用计数+1的情况:

    • 对象被创建,例如a = 23
    • 对象被引用,例如b = a
    • 对象被作为参数,传入到一个函数中,例如f(a)
    • 对象作为一个元素,存储在容器中,例如list1 = [a, a]

    导致引用计数-1的情况:

    • 对象的别名被显式销毁,例如del a
    • 对象的别名被赋予新的对象,例如a = 24
    • 一个对象离开它的作用域,例如f函数执行完毕时,f函数中的局部变量(全局变量不会)
    • 对象所在的容器被销毁,或从容器中删除对象

    引用计数可能会导致循环引用问题,而循环引用会导致内存泄露,如下面的代码所示。为了解决这个问题,Python中引入了“标记-清除”和“分代收集”。在创建一个对象的时候,对象被放在第一代中,如果在第一代的垃圾检查中对象存活了下来,该对象就会被放到第二代中,同理在第二代的垃圾检查中对象存活下来,该对象就会被放到第三代中。

    # 循环引用会导致内存泄露 - Python除了引用技术还引入了标记清理和分代回收
    # 在Python 3.6以前如果重写__del__魔术方法会导致循环引用处理失效
    # 如果不想造成循环引用可以使用弱引用
    list1 = []
    list2 = [] 
    list1.append(list2)
    list2.append(list1)
    

    以下情况会导致垃圾回收:

    • 调用gc.collect()
    • gc模块的计数器达到阀值
    • 程序退出

    如果循环引用中两个对象都定义了__del__方法,gc模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的__del__方法,这个问题在Python 3.6中得到了解决。

    也可以通过weakref模块构造弱引用的方式来解决循环引用的问题。

  • 魔法属性和方法(请参考《Python魔法方法指南》)

    有几个小问题请大家思考:

    • 自定义的对象能不能使用运算符做运算?
    • 自定义的对象能不能放到set中?能去重吗?
    • 自定义的对象能不能作为dict的键?
    • 自定义的对象能不能使用上下文语法?
  • 混入(Mixin)

    例子:自定义字典限制只有在指定的key不存在时才能在字典中设置键值对。

    class SetOnceMappingMixin:
        """自定义混入类"""
        __slots__ = ()
    
        def __setitem__(self, key, value):
            if key in self:
                raise KeyError(str(key) + ' already set')
            return super().__setitem__(key, value)
    
    
    class SetOnceDict(SetOnceMappingMixin, dict):
        """自定义字典"""
        pass
    
    
    my_dict= SetOnceDict()
    try:
        my_dict['username'] = 'jackfrued'
        my_dict['username'] = 'hellokitty'
    except KeyError:
        pass
    print(my_dict)
    
  • 元编程和元类

    例子:用元类实现单例模式。

    import threading
    
    
    class SingletonMeta(type):
        """自定义元类"""
    
        def __init__(cls, *args, **kwargs):
            cls.__instance = None
            cls.__lock = threading.Lock()
            super().__init__(*args, **kwargs)
    
        def __call__(cls, *args, **kwargs):
            if cls.__instance is None:
                with cls.__lock:
                    if cls.__instance is None:
                        cls.__instance = super().__call__(*args, **kwargs)
            return cls.__instance
    
    
    class President(metaclass=SingletonMeta):
        """总统(单例类)"""
        
        pass
    
  • 面向对象设计原则

    • 单一职责原则 (SRP)- 一个类只做该做的事情(类的设计要高内聚)
    • 开闭原则 (OCP)- 软件实体应该对扩展开发对修改关闭
    • 依赖倒转原则(DIP)- 面向抽象编程(在弱类型语言中已经被弱化)
    • 里氏替换原则(LSP) - 任何时候可以用子类对象替换掉父类对象
    • 接口隔离原则(ISP)- 接口要小而专不要大而全(Python中没有接口的概念)
    • 合成聚合复用原则(CARP) - 优先使用强关联关系而不是继承关系复用代码
    • 最少知识原则(迪米特法则,LoD)- 不要给没有必然联系的对象发消息

    说明:上面加粗的字母放在一起称为面向对象的SOLID原则。

  • GoF设计模式

    • 创建型模式:单例、工厂、建造者、原型
    • 结构型模式:适配器、门面(外观)、代理
    • 行为型模式:迭代器、观察者、状态、策略

    例子:可插拔的哈希算法。

    class StreamHasher():
        """哈希摘要生成器(策略模式)"""
    
        def __init__(self, alg='md5', size=4096):
            self.size = size
            alg = alg.lower()
            self.hasher = getattr(__import__('hashlib'), alg.lower())()
    
        def __call__(self, stream):
            return self.to_digest(stream)
    
        def to_digest(self, stream):
            """生成十六进制形式的摘要"""
            for buf in iter(lambda: stream.read(self.size), b''):
                self.hasher.update(buf)
            return self.hasher.hexdigest()
    
    def main():
        """主函数"""
        hasher1 = StreamHasher()
        with open('Python-3.7.1.tgz', 'rb') as stream:
            print(hasher1.to_digest(stream))
        hasher2 = StreamHasher('sha1')
        with open('Python-3.7.1.tgz', 'rb') as stream:
            print(hasher2(stream))
    
    
    if __name__ == '__main__':
        main()
    

迭代器和生成器

  • 和迭代器相关的魔术方法(__iter____next__

  • 两种创建生成器的方式(生成器表达式和yield关键字)

    def fib(num):
        """生成器"""
        a, b = 0, 1
        for _ in range(num):
            a, b = b, a + b
            yield a
       
       
    class Fib(object):
        """迭代器"""
        
        def __init__(self, num):
            self.num = num
            self.a, self.b = 0, 1
            self.idx = 0
       
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.idx < self.num:
                self.a, self.b = self.b, self.a + self.b
                self.idx += 1
                return self.a
            raise StopIteration()
    

并发编程

Python中实现并发编程的三种方案:多线程、多进程和异步I/O。并发编程的好处在于可以提升程序的执行效率以及改善用户体验;坏处在于并发的程序不容易开发和调试,同时对其他程序来说它并不友好。

  • 多线程:Python中提供了Thread类并辅以Lock、Condition、Event、Semaphore和Barrier。Python中有GIL来防止多个线程同时执行本地字节码,这个锁对于CPython是必须的,因为CPython的内存管理并不是线程安全的,因为GIL的存在多线程并不能发挥CPU的多核特性。

    """
    面试题:进程和线程的区别和联系?
    进程 - 操作系统分配内存的基本单位 - 一个进程可以包含一个或多个线程
    线程 - 操作系统分配CPU的基本单位
    并发编程(concurrent programming)
    1. 提升执行性能 - 让程序中没有因果关系的部分可以并发的执行
    2. 改善用户体验 - 让耗时间的操作不会造成程序的假死
    """
    import glob
    import os
    import threading
    
    from PIL import Image
    
    PREFIX = 'thumbnails'
    
    
    def generate_thumbnail(infile, size, format='PNG'):
        """生成指定图片文件的缩略图"""
       file, ext = os.path.splitext(infile)
       file = file[file.rfind('/') + 1:]
       outfile = f'{PREFIX}/{file}_{size[0]}_{size[1]}.{ext}'
       img = Image.open(infile)
       img.thumbnail(size, Image.ANTIALIAS)
       img.save(outfile, format)
    
    
    def main():
        """主函数"""
       if not os.path.exists(PREFIX):
           os.mkdir(PREFIX)
       for infile in glob.glob('images/*.png'):
           for size in (32, 64, 128):
                # 创建并启动线程
               threading.Thread(
                   target=generate_thumbnail, 
                   args=(infile, (size, size))
               ).start()
               
    
    if __name__ == '__main__':
       main()
    

    多个线程竞争资源的情况

    """
    多线程程序如果没有竞争资源处理起来通常也比较简单
    当多个线程竞争临界资源的时候如果缺乏必要的保护措施就会导致数据错乱
    说明:临界资源就是被多个线程竞争的资源
    """
    import time
    import threading
    
    from concurrent.futures import ThreadPoolExecutor
    
    
    class Account(object):
        """银行账户"""
    
        def __init__(self):
            self.balance = 0.0
            self.lock = threading.Lock()
    
        def deposit(self, money):
            # 通过锁保护临界资源
            with self.lock:
                new_balance = self.balance + money
                time.sleep(0.001)
                self.balance = new_balance
    
    
    class AddMoneyThread(threading.Thread):
        """自定义线程类"""
    
        def __init__(self, account, money):
            self.account = account
            self.money = money
            # 自定义线程的初始化方法中必须调用父类的初始化方法
            super().__init__()
    
        def run(self):
            # 线程启动之后要执行的操作
            self.account.deposit(self.money)
    
    def main():
        """主函数"""
        account = Account()
        # 创建线程池
        pool = ThreadPoolExecutor(max_workers=10)
        futures = []
        for _ in range(100):
            # 创建线程的第1种方式
            # threading.Thread(
            #     target=account.deposit, args=(1, )
            # ).start()
            # 创建线程的第2种方式
            # AddMoneyThread(account, 1).start()
            # 创建线程的第3种方式
            # 调用线程池中的线程来执行特定的任务
            future = pool.submit(account.deposit, 1)
            futures.append(future)
        # 关闭线程池
        pool.shutdown()
        for future in futures:
            future.result()
        print(account.balance)
    
    
    if __name__ == '__main__':
        main()
    

    修改上面的程序,启动5个线程向账户中存钱,5个线程从账户中取钱,取钱时如果余额不足就暂停线程进行等待。为了达到上述目标,需要对存钱和取钱的线程进行调度,在余额不足时取钱的线程暂停并释放锁,而存钱的线程将钱存入后要通知取钱的线程,使其从暂停状态被唤醒。可以使用threading模块的Condition来实现线程调度,该对象也是基于锁来创建的,代码如下所示:

    """
    多个线程竞争一个资源 - 保护临界资源 - 锁(Lock/RLock)
    多个线程竞争多个资源(线程数>资源数) - 信号量(Semaphore)
    多个线程的调度 - 暂停线程执行/唤醒等待中的线程 - Condition
    """
    from concurrent.futures import ThreadPoolExecutor
    from random import randint
    from time import sleep
    
    import threading
    
    
    class Account():
        """银行账户"""
    
        def __init__(self, balance=0):
            self.balance = balance
            lock = threading.Lock()
            self.condition = threading.Condition(lock)
    
        def withdraw(self, money):
            """取钱"""
            with self.condition:
                while money > self.balance:
                    self.condition.wait()
                new_balance = self.balance - money
                sleep(0.001)
                self.balance = new_balance
    
        def deposit(self, money):
            """存钱"""
            with self.condition:
                new_balance = self.balance + money
                sleep(0.001)
                self.balance = new_balance
                self.condition.notify_all()
    
    
    def add_money(account):
        while True:
            money = randint(5, 10)
            account.deposit(money)
            print(threading.current_thread().name, 
                  ':', money, '====>', account.balance)
            sleep(0.5)
    
    
    def sub_money(account):
        while True:
            money = randint(10, 30)
            account.withdraw(money)
            print(threading.current_thread().name, 
                  ':', money, '<====', account.balance)
            sleep(1)
    
    
    def main():
        account = Account()
        with ThreadPoolExecutor(max_workers=10) as pool:
            for _ in range(5):
                pool.submit(add_money, account)
                pool.submit(sub_money, account)
    
    
    if __name__ == '__main__':
        main()
    
  • 多进程:多进程可以有效的解决GIL的问题,实现多进程主要的类是Process,其他辅助的类跟threading模块中的类似,进程间共享数据可以使用管道、套接字等,在multiprocessing模块中有一个Queue类,它基于管道和锁机制提供了多个进程共享的队列。下面是官方文档上关于多进程和进程池的一个示例。

    """
    多进程和进程池的使用
    多线程因为GIL的存在不能够发挥CPU的多核特性
    对于计算密集型任务应该考虑使用多进程
    time python3 example22.py
    real    0m11.512s
    user    0m39.319s
    sys     0m0.169s
    使用多进程后实际执行时间为11.512秒,而用户时间39.319秒约为实际执行时间的4倍
    这就证明我们的程序通过多进程使用了CPU的多核特性,而且这台计算机配置了4核的CPU
    """
    import concurrent.futures
    import math
    
    PRIMES = [
        1116281,
        1297337,
        104395303,
        472882027,
        533000389,
        817504243,
        982451653,
        112272535095293,
        112582705942171,
        112272535095293,
        115280095190773,
        115797848077099,
        1099726899285419
    ] * 5
    
    
    def is_prime(n):
        """判断素数"""
        if n % 2 == 0:
            return False
    
        sqrt_n = int(math.floor(math.sqrt(n)))
        for i in range(3, sqrt_n + 1, 2):
            if n % i == 0:
                return False
        return True
    
    
    def main():
        """主函数"""
        with concurrent.futures.ProcessPoolExecutor() as executor:
            for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
                print('%d is prime: %s' % (number, prime))
    
    
    if __name__ == '__main__':
        main()
    

    说明:多线程和多进程的比较

    以下情况需要使用多线程:

    1. 程序需要维护许多共享的状态(尤其是可变状态),Python中的列表、字典、集合都是线程安全的,所以使用线程而不是进程维护共享状态的代价相对较小。
    2. 程序会花费大量时间在I/O操作上,没有太多并行计算的需求且不需占用太多的内存。

    以下情况需要使用多进程:

    1. 程序执行计算密集型任务(如:字节码操作、数据处理、科学计算)。
    2. 程序的输入可以并行的分成块,并且可以将运算结果合并。
    3. 程序在内存使用方面没有任何限制且不强依赖于I/O操作(如:读写文件、套接字等)。
  • 异步处理:从调度程序的任务队列中挑选任务,该调度程序以交叉的形式执行这些任务,我们并不能保证任务将以某种顺序去执行,因为执行顺序取决于队列中的一项任务是否愿意将CPU处理时间让位给另一项任务。异步任务通常通过多任务协作处理的方式来实现,由于执行时间和顺序的不确定,因此需要通过回调式编程或者future对象来获取任务执行的结果。Python 3通过asyncio模块和awaitasync关键字(在Python 3.7中正式被列为关键字)来支持异步处理。

    """
    异步I/O - async / await
    """
    import asyncio
    
    
    def num_generator(m, n):
        """指定范围的数字生成器"""
        yield from range(m, n + 1)
    
    
    async def prime_filter(m, n):
        """素数过滤器"""
        primes = []
        for i in num_generator(m, n):
            flag = True
            for j in range(2, int(i ** 0.5 + 1)):
                if i % j == 0:
                    flag = False
                    break
            if flag:
                print('Prime =>', i)
                primes.append(i)
    
            await asyncio.sleep(0.001)
        return tuple(primes)
    
    
    async def square_mapper(m, n):
        """平方映射器"""
        squares = []
        for i in num_generator(m, n):
            print('Square =>', i * i)
            squares.append(i * i)
    
            await asyncio.sleep(0.001)
        return squares
    
    
    def main():
        """主函数"""
        loop = asyncio.get_event_loop()
        future = asyncio.gather(prime_filter(2, 100), square_mapper(1, 100))
        future.add_done_callback(lambda x: print(x.result()))
        loop.run_until_complete(future)
        loop.close()
    
    
    if __name__ == '__main__':
        main()
    

    说明:上面的代码使用get_event_loop函数获得系统默认的事件循环,通过gather函数可以获得一个future对象,future对象的add_done_callback可以添加执行完成时的回调函数,loop对象的run_until_complete方法可以等待通过future对象获得协程执行结果。

    Python中有一个名为aiohttp的三方库,它提供了异步的HTTP客户端和服务器,这个三方库可以跟asyncio模块一起工作,并提供了对Future对象的支持。Python 3.6中引入了async和await来定义异步执行的函数以及创建异步上下文,在Python 3.7中它们正式成为了关键字。下面的代码异步的从5个URL中获取页面并通过正则表达式的命名捕获组提取了网站的标题。

    import asyncio
    import re
    
    import aiohttp
    
    PATTERN = re.compile(r'\(?P.*)\<\/title\>')
    
    
    async def fetch_page(session, url):
        async with session.get(url, ssl=False) as resp:
            return await resp.text()
    
    
    async def show_title(url):
        async with aiohttp.ClientSession() as session:
            html = await fetch_page(session, url)
            print(PATTERN.search(html).group('title'))
    
    
    def main():
        urls = ('https://www.python.org/',
                'https://git-scm.com/',
                'https://www.jd.com/',
                'https://www.taobao.com/',
                'https://www.douban.com/')
        loop = asyncio.get_event_loop()
        tasks = [show_title(url) for url in urls]
        loop.run_until_complete(asyncio.wait(tasks))
        loop.close()
    
    
    if __name__ == '__main__':
        main()
    </code></pre> 
       <blockquote> 
        <p>说明:<strong>异步I/O与多进程的比较</strong>。</p> 
        <p>当程序不需要真正的并发性或并行性,而是更多的依赖于异步处理和回调时,asyncio就是一种很好的选择。如果程序中有大量的等待与休眠时,也应该考虑asyncio,它很适合编写没有实时数据处理需求的Web应用服务器。</p> 
       </blockquote> <p>Python还有很多用于处理并行任务的三方库,例如:joblib、PyMP等。实际开发中,要提升系统的可扩展性和并发性通常有垂直扩展(增加单个节点的处理能力)和水平扩展(将单个节点变成多个节点)两种做法。可以通过消息队列来实现应用程序的解耦合,消息队列相当于是多线程同步队列的扩展版本,不同机器上的应用程序相当于就是线程,而共享的分布式消息队列就是原来程序中的Queue。消息队列(面向消息的中间件)的最流行和最标准化的实现是AMQP(高级消息队列协议),AMQP源于金融行业,提供了排队、路由、可靠传输、安全等功能,最著名的实现包括:Apache的ActiveMQ、RabbitMQ等。</p> <p>要实现任务的异步化,可以使用名为Celery的三方库。Celery是Python编写的分布式任务队列,它使用分布式消息进行工作,可以基于RabbitMQ或Redis来作为后端的消息代理。</p> </li> 
     </ul> 
    </article>
                                </div>
                            </div>
                        </div>
                        <!--PC和WAP自适应版-->
                        <div id="SOHUCS" sid="1700010123684032512"></div>
                        <script type="text/javascript" src="/views/front/js/chanyan.js"></script>
                        <!-- 文章页-底部 动态广告位 -->
                        <div class="youdao-fixed-ad" id="detail_ad_bottom"></div>
                    </div>
                    <div class="col-md-3">
                        <div class="row" id="ad">
                            <!-- 文章页-右侧1 动态广告位 -->
                            <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                                <div class="youdao-fixed-ad" id="detail_ad_1"> </div>
                            </div>
                            <!-- 文章页-右侧2 动态广告位 -->
                            <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                                <div class="youdao-fixed-ad" id="detail_ad_2"></div>
                            </div>
                            <!-- 文章页-右侧3 动态广告位 -->
                            <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                                <div class="youdao-fixed-ad" id="detail_ad_3"></div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="container">
            <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(Python语言进阶)</h4>
            <div id="paradigm-article-related">
                <div class="recommend-post mb30">
                    <ul class="widget-links">
                        <li><a href="/article/1938842215090548736.htm"
                               title="如何安装 `.whl` 文件(Python Wheel 包)" target="_blank">如何安装 `.whl` 文件(Python Wheel 包)</a>
                            <span class="text-muted">喝醉酒的小白</span>
    <a class="tag" taget="_blank" href="/search/Liunx/1.htm">Liunx</a><a class="tag" taget="_blank" href="/search/Python%E6%A8%A1%E5%9D%97/1.htm">Python模块</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                            <div>目录标题如何安装`.whl`文件(PythonWheel包)安装前提安装方法(3种)方法1:直接使用pip安装(推荐)方法2:先进入文件目录再安装方法3:使用绝对路径(适合脚本中调用)⚠️常见问题解决问题1:版本不兼容错误问题2:缺少依赖问题3:权限不足验证安装进阶技巧如何安装.whl文件(PythonWheel包).whl文件是Python的二进制分发格式(Wheel格式),用于快速安装Pyth</div>
                        </li>
                        <li><a href="/article/1938809444083232768.htm"
                               title="Node.js特训专栏-实战进阶:8. Express RESTful API设计规范与实现" target="_blank">Node.js特训专栏-实战进阶:8. Express RESTful API设计规范与实现</a>
                            <span class="text-muted">爱分享的程序员</span>
    <a class="tag" taget="_blank" href="/search/Node.js/1.htm">Node.js</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/node.js/1.htm">node.js</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                            <div>欢迎来到Node.js实战专栏!在这里,每一行代码都是解锁高性能应用的钥匙,让我们一起开启Node.js的奇妙开发之旅!Node.js特训专栏主页专栏内容规划详情ExpressRESTfulAPI设计规范与实现:构建标准化、可维护的接口服务在前后端分离架构盛行的今天,RESTfulAPI已成为Web服务交互的事实标准。基于Express框架构建RESTfulAPI,既能利用Node.js的高效性能</div>
                        </li>
                        <li><a href="/article/1938735576190611456.htm"
                               title="C语言到C++快速入门" target="_blank">C语言到C++快速入门</a>
                            <span class="text-muted"></span>
    
                            <div>前言:通过前面的学习,我们了解了C语言的一些性质和用法,为了更加深入的学习C,我们可以向C++进阶,探究C++的知识世界,相信可以收获不少知识!一.C语言和C++的关系:起源与发展:C语言是由DennisRitchie在1970年代初期开发的,它最初是为了重新设计UNIX操作系统而创建的。C++则是在C语言的基础上发展而来的,由BjarneStroustrup在1980年代初期开始设计,其目标是增</div>
                        </li>
                        <li><a href="/article/1938687428080496640.htm"
                               title="【ASP.NET Core】配置应用程序地址的N多种方法" target="_blank">【ASP.NET Core】配置应用程序地址的N多种方法</a>
                            <span class="text-muted">pythonxxoo</span>
    <a class="tag" taget="_blank" href="/search/%E8%AE%A1%E7%AE%97%E6%9C%BA/1.htm">计算机</a><a class="tag" taget="_blank" href="/search/asp.net/1.htm">asp.net</a><a class="tag" taget="_blank" href="/search/flask/1.htm">flask</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E8%AE%A1%E7%AE%97%E6%9C%BA/1.htm">计算机</a>
                            <div>优质资源分享学习路线指引(点击解锁)知识定位人群定位Python实战微信订餐小程序进阶级本课程是pythonflask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。Python量化交易实战入门级手把手带你打造一个易扩展、更安全、效率更高的量化交易系统下面又到了老周误人子弟的时间,今天要误大伙的话题是:找找有多少种方法可以设置ASP.NETCore应用的地址,即URL。精</div>
                        </li>
                        <li><a href="/article/1938675709719408640.htm"
                               title="Whistle 超详细技术博客:原理、配置、用法与进阶技巧全解" target="_blank">Whistle 超详细技术博客:原理、配置、用法与进阶技巧全解</a>
                            <span class="text-muted">北漂老男人</span>
    <a class="tag" taget="_blank" href="/search/%E6%8A%93%E5%8C%85%E5%B7%A5%E5%85%B7/1.htm">抓包工具</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a>
                            <div>Whistle超详细技术博客:原理、配置、用法与进阶技巧全解目录Whistle简介与应用场景Whistle安装与启动Whistle原理与架构Whistle规则语法详解常用配置与实战场景Whistle进阶用法与技巧常见问题与排查实用插件推荐总结与参考资料1.Whistle简介与应用场景Whistle是一款基于Node.js的跨平台Web调试代理工具,功能类似于Charles、Fiddler,但更轻量</div>
                        </li>
                        <li><a href="/article/1938641424962613248.htm"
                               title="数据结构进阶 第七章 图(Graph)" target="_blank">数据结构进阶 第七章 图(Graph)</a>
                            <span class="text-muted">an_胺</span>
    <a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E8%BF%9B%E9%98%B6/1.htm">数据结构进阶</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88/1.htm">深度优先</a><a class="tag" taget="_blank" href="/search/%E5%9B%BE%E8%AE%BA/1.htm">图论</a>
                            <div>第7章图(Graph)7.1图的基本术语图的定义图是由顶点集合V和边集合E组成的数据结构,记为G=(V,E),其中:顶点集V:有限非空集合边集E:顶点对的集合基本概念无向图:边没有方向,用无序对(vi,vj)表示有向图:边有方向,用有序对表示完全图:任意两个顶点之间都有边稀疏图:边数相对较少的图,|E|vexnum,&G->arcnum);for(i=0;ivexnum;i++){scanf(&G</div>
                        </li>
                        <li><a href="/article/1938639909841596416.htm"
                               title="Java进阶-查找算法" target="_blank">Java进阶-查找算法</a>
                            <span class="text-muted">晚风烟火</span>
    <a class="tag" taget="_blank" href="/search/JavaSE%E7%AC%94%E8%AE%B0/1.htm">JavaSE笔记</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a>
                            <div>常见的七种查找算法:1.基本查找也叫做顺序查找说明:顺序查找适合于存储结构为数组或者链表。基本思想:顺序查找也称为线形查找,属于无序查找算法。从数据结构线的一端开始,顺序扫描,依次将遍历到的结点与要查找的值相比较,若相等则表示查找成功;若遍历结束仍没有找到相同的,表示查找失败。示例代码:publicclassA01_BasicSearchDemo1{publicstaticvoidmain(Str</div>
                        </li>
                        <li><a href="/article/1938639027594915840.htm"
                               title="python打卡:day1" target="_blank">python打卡:day1</a>
                            <span class="text-muted">灯眠joy</span>
    <a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                            <div>变量与格式化字符串题目1a=1b=2c=3print(a,b,c,sep='\n')##用换行符作为分隔符123这里可以看出python语言相比于其他语言更加自由,在变量定义上不需要声明变量类型。题目2a,b,c=1,2,3print(a,b,c)1,2,3这里可以看出在变量定义上python可以一次定义多个变量print(a)print(b)print(c)123格式化字符串name=”小明“#</div>
                        </li>
                        <li><a href="/article/1938631341667053568.htm"
                               title="创意Python爱心代码" target="_blank">创意Python爱心代码</a>
                            <span class="text-muted">卖血买老婆</span>
    <a class="tag" taget="_blank" href="/search/Python%E4%B8%93%E6%A0%8F/1.htm">Python专栏</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                            <div>目录一、用字符在控制台打印爱心图案1.1方法1:简单星号爱心说明1.2方法2:调整字符和形状二、turtle绘制爱心2.1turtle画心形及写字说明2.2动态跳动爱心三、用Matplotlib画心形曲线3.1标准心形曲线3.2LOVE动画心形(进阶)四、参数方程自定义爱心(数学美)心形参数方程公式五、更多创意:二维码嵌入、爱心表白墙六、总结完整参考目录用Python创意绘制爱心(Heart)的多</div>
                        </li>
                        <li><a href="/article/1938625667268669440.htm"
                               title="鸿蒙开发进阶(HarmonyOS)订阅资源泄漏事件(C/C++)" target="_blank">鸿蒙开发进阶(HarmonyOS)订阅资源泄漏事件(C/C++)</a>
                            <span class="text-muted">你不要给我蛙蛙叫啊</span>
    <a class="tag" taget="_blank" href="/search/%E9%B8%BF%E8%92%99%E5%BC%80%E5%8F%91/1.htm">鸿蒙开发</a><a class="tag" taget="_blank" href="/search/HarmonyOS/1.htm">HarmonyOS</a><a class="tag" taget="_blank" href="/search/OpenHarmony/1.htm">OpenHarmony</a><a class="tag" taget="_blank" href="/search/harmonyos/1.htm">harmonyos</a><a class="tag" taget="_blank" href="/search/%E5%8D%8E%E4%B8%BA/1.htm">华为</a><a class="tag" taget="_blank" href="/search/1024%E7%A8%8B%E5%BA%8F%E5%91%98%E8%8A%82/1.htm">1024程序员节</a><a class="tag" taget="_blank" href="/search/%E9%B8%BF%E8%92%99/1.htm">鸿蒙</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/ui/1.htm">ui</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                            <div>鸿蒙NEXT开发实战往期必看文章:一分钟了解”纯血版!鸿蒙HarmonyOSNext应用开发!“非常详细的”鸿蒙HarmonyOSNext应用开发学习路线!(从零基础入门到精通)HarmonyOSNEXT应用开发案例实践总结合(持续更新......)HarmonyOSNEXT应用开发性能优化实践总结(持续更新......)接口说明API接口的具体使用说明(参数使用限制、具体取值范围等)请参考HiA</div>
                        </li>
                        <li><a href="/article/1938612427188924416.htm"
                               title="从开源到创业:掌握 Websoft9 托管平台上的开源工具,就业到创业的路径" target="_blank">从开源到创业:掌握 Websoft9 托管平台上的开源工具,就业到创业的路径</a>
                            <span class="text-muted"></span>
    
                            <div>从开源实践到商业洞察:基于Websoft9的学生能力进阶路径引言:开源工具的“双螺旋”价值在开源生态与云原生技术融合的今天,学生群体通过平台化工具实践开源项目,正在突破传统“技术学习-就业”的单线程路径。Websoft9这类集成200+开源应用的自动化部署平台,不仅降低了技术实践门槛,更通过场景化部署、业务模拟、服务延伸构建起从技术实操到商业验证的闭环。本文将解析学生如何通过四阶段进阶,实现技术能</div>
                        </li>
                        <li><a href="/article/1938605618315194368.htm"
                               title="Vue3项目引入高德地图【超详细教程】" target="_blank">Vue3项目引入高德地图【超详细教程】</a>
                            <span class="text-muted">年纪轻轻就扛不住</span>
    <a class="tag" taget="_blank" href="/search/VUE/1.htm">VUE</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/vue/1.htm">vue</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6/1.htm">前端框架</a>
                            <div>前言在Vue3项目中集成高德地图(AMap)是一个常见的需求。本文将详细介绍如何在Vue3项目中使用高德地图,包括安装配置、基本使用以及一些进阶功能的实现。一、环境准备1.1vue3项目初始化步骤1:初始化项目npmcreatevite@latest步骤2:按提示选择配置✔Projectname:…your-project-name#输入项目名称✔Selectaframework:›Vue#选择框</div>
                        </li>
                        <li><a href="/article/1938528336124899328.htm"
                               title="C#上位机开发进阶:多协议融合通信(S7、Modbus TCP、OPC UA)与西门子PLC联动" target="_blank">C#上位机开发进阶:多协议融合通信(S7、Modbus TCP、OPC UA)与西门子PLC联动</a>
                            <span class="text-muted">威哥说编程</span>
    <a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a><a class="tag" taget="_blank" href="/search/tcp%2Fip/1.htm">tcp/ip</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/1.htm">网络协议</a><a class="tag" taget="_blank" href="/search/c%23/1.htm">c#</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                            <div>随着工业自动化和智能制造的快速发展,不同协议的设备和系统需要高效协同工作。这不仅要求我们能够与西门子PLC等设备进行有效通信,还需要能够跨越不同协议进行数据交换和设备控制。S7协议、ModbusTCP协议以及OPCUA协议是工业自动化领域中广泛应用的协议,而C#作为强大的开发语言,提供了丰富的库和工具来实现这些协议的融合通信。本文将通过实际示例,介绍如何在C#中实现多协议融合通信(S7、Modbu</div>
                        </li>
                        <li><a href="/article/1938493549511241728.htm"
                               title="高级 Python 测试工程师学习提升计划" target="_blank">高级 Python 测试工程师学习提升计划</a>
                            <span class="text-muted">code36</span>
    <a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E6%B5%8B%E8%AF%95/1.htm">测试</a><a class="tag" taget="_blank" href="/search/%E7%88%AC%E8%99%AB/1.htm">爬虫</a><a class="tag" taget="_blank" href="/search/%E9%AB%98%E7%BA%A7%E6%B5%8B%E8%AF%95/1.htm">高级测试</a>
                            <div>一、测试理论与流程夯实系统梳理:每周安排3-4小时,深入研读软件测试的艺术、Google软件测试之道,重点强化功能、性能、安全性测试流程,整理流程关键节点与执行要点笔记。实践模拟:基于线上开源项目(如GitHub找小型Web应用),每月开展2次全流程测试实践,从需求分析到测试报告输出,巩固理论应用。二、Python及测试工具深化Python进阶:利用Python高级课程资料,主攻面向对象编程、装饰</div>
                        </li>
                        <li><a href="/article/1938492666710913024.htm"
                               title="CMake实战指南:从入门到放弃(不是)的完整教程 [特殊字符]" target="_blank">CMake实战指南:从入门到放弃(不是)的完整教程 [特殊字符]</a>
                            <span class="text-muted"></span>
    
                            <div>文章目录一、为什么你的项目需要CMake?(灵魂拷问)二、5分钟极速安装指南Windows党看这里!Linux用户一条龙服务MacOS优雅安装法三、第一个CMake项目实战️CMakeLists.txt编写(重点中的重点!)main.cpp内容四、构建项目的正确姿势️五、CMake进阶技巧(装逼必备)1.多目录项目组织2.第三方库集成3.条件编译六、新手常见坑点大全️报错1:CMakeErrora</div>
                        </li>
                        <li><a href="/article/1938432030077415424.htm"
                               title="【高频考点精讲】CSS动画性能优化:为什么你的动画卡顿?这些技巧让你的页面丝般顺滑" target="_blank">【高频考点精讲】CSS动画性能优化:为什么你的动画卡顿?这些技巧让你的页面丝般顺滑</a>
                            <span class="text-muted">全栈老李技术面试</span>
    <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E9%AB%98%E9%A2%91%E8%80%83%E7%82%B9%E7%B2%BE%E8%AE%B2/1.htm">前端高频考点精讲</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95%E9%A2%98/1.htm">面试题</a><a class="tag" taget="_blank" href="/search/react/1.htm">react</a><a class="tag" taget="_blank" href="/search/vue/1.htm">vue</a>
                            <div>CSS动画性能优化:为什么你的动画卡顿?这些技巧让你的页面丝般顺滑‍作者:全栈老李更新时间:2025年6月‍适合人群:前端初学者、进阶开发者版权:本文由全栈老李原创,转载请注明出处。最近在项目review时,发现不少同学写的CSS动画总是一卡一卡的,像极了老式DVD播放器卡碟的样子。今天全栈老李就来聊聊,如何让你的CSS动画像德芙巧克力广告那样——纵享丝滑。为什么你的动画会卡成PPT?浏览器渲染一</div>
                        </li>
                        <li><a href="/article/1938431903870808064.htm"
                               title="【高频考点精讲】前端AI绘画实战:从Stable Diffusion到Web集成" target="_blank">【高频考点精讲】前端AI绘画实战:从Stable Diffusion到Web集成</a>
                            <span class="text-muted">全栈老李技术面试</span>
    <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E9%AB%98%E9%A2%91%E8%80%83%E7%82%B9%E7%B2%BE%E8%AE%B2/1.htm">前端高频考点精讲</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95%E9%A2%98/1.htm">面试题</a><a class="tag" taget="_blank" href="/search/react/1.htm">react</a><a class="tag" taget="_blank" href="/search/vue/1.htm">vue</a>
                            <div>前端AI绘画实战:从StableDiffusion到Web集成‍作者:全栈老李更新时间:2025年5月‍适合人群:前端初学者、进阶开发者版权:本文由全栈老李原创,转载请注明出处。今天咱们聊聊一个让设计师瑟瑟发抖的话题——如何用前端技术把AI绘画能力集成到你的Web应用中。我是全栈老李,一个喜欢把复杂技术讲简单的老码农。最近有个做电商的朋友找我吐槽:"老李啊,我们每天要生成几百张商品场景图,设计师都</div>
                        </li>
                        <li><a href="/article/1938431777492234240.htm"
                               title="【高频考点精讲】前端路由的两种实现原理:hash模式和history模式对比" target="_blank">【高频考点精讲】前端路由的两种实现原理:hash模式和history模式对比</a>
                            <span class="text-muted">全栈老李技术面试</span>
    <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E9%AB%98%E9%A2%91%E8%80%83%E7%82%B9%E7%B2%BE%E8%AE%B2/1.htm">前端高频考点精讲</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95%E9%A2%98/1.htm">面试题</a><a class="tag" taget="_blank" href="/search/react/1.htm">react</a><a class="tag" taget="_blank" href="/search/vue/1.htm">vue</a>
                            <div>前端路由的两种实现原理:hash模式与history模式深度解析‍作者:全栈老李更新时间:2025年5月‍适合人群:前端初学者、进阶开发者版权:本文由全栈老李原创,转载请注明出处。大家好,我是全栈老李。今天咱们来聊聊前端路由这个话题,这是现代单页应用(SPA)开发中绕不开的核心概念。路由就像是你家小区的导航系统,告诉访客该往哪走才能找到你家。而在前端世界里,路由就是告诉浏览器该显示哪个"页面",虽</div>
                        </li>
                        <li><a href="/article/1938431776405909504.htm"
                               title="【高频考点精讲】前端AI集成实战:从TensorFlow.js到模型部署" target="_blank">【高频考点精讲】前端AI集成实战:从TensorFlow.js到模型部署</a>
                            <span class="text-muted">全栈老李技术面试</span>
    <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E9%AB%98%E9%A2%91%E8%80%83%E7%82%B9%E7%B2%BE%E8%AE%B2/1.htm">前端高频考点精讲</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95%E9%A2%98/1.htm">面试题</a><a class="tag" taget="_blank" href="/search/react/1.htm">react</a><a class="tag" taget="_blank" href="/search/vue/1.htm">vue</a>
                            <div>前端AI集成实战:从TensorFlow.js到模型部署‍作者:全栈老李更新时间:2025年5月‍适合人群:前端初学者、进阶开发者版权:本文由全栈老李原创,转载请注明出处。今天咱们聊聊前端工程师如何玩转AI——没错,用JavaScript就能搞机器学习!我是全栈老李,一个喜欢把复杂技术讲简单的实战派。最近发现不少前端同学对AI既好奇又害怕,其实真没想象中那么难,跟着老李走,30分钟让你亲手部署第一</div>
                        </li>
                        <li><a href="/article/1938400636924129280.htm"
                               title="PHP基础3(错误处理,正则表达式,反序列化,操作mysql,cookie和session)" target="_blank">PHP基础3(错误处理,正则表达式,反序列化,操作mysql,cookie和session)</a>
                            <span class="text-muted">往日情怀酿作酒yx</span>
    <a class="tag" taget="_blank" href="/search/php%E5%9F%BA%E7%A1%80/1.htm">php基础</a><a class="tag" taget="_blank" href="/search/php/1.htm">php</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                            <div>一.前言前面说了函数和流程控制那些,今天来说一些进阶一点的,错误处理之类的,上一节内容特别多,本章节内容将会少很多。二.PHP的错误处理很多时候,代码如果写的不太好,那么很容易报错。那么如果遇到了错误,我们应该想办法捕获到这个错误并记录下来,而且最好不要用户看到,不然太尴尬了,而且容易暴漏自己服务端的一些敏感信息,方便我们后续修改,并且尽量不要让整个程序因为一点小错误而崩溃。2.1配置项管理在ph</div>
                        </li>
                        <li><a href="/article/1938375904568864768.htm"
                               title="# Flask:Python的轻量级Web框架入门之旅(超级实用!)" target="_blank"># Flask:Python的轻量级Web框架入门之旅(超级实用!)</a>
                            <span class="text-muted">果果了不起</span>
    <a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/flask/1.htm">flask</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E5%85%B6%E4%BB%96/1.htm">其他</a>
                            <div>文章目录Flask到底是个啥玩意儿?为啥Flask这么受欢迎?(个人亲测优势)手把手教你建第一个Flask应用(零基础也能行)进阶技巧:模板和扩展(让应用更专业)实战应用场景(个人项目灵感)我的真心话:为什么爱上Flask嘿,大家好!今天我想聊聊Flask——那个Python世界里超酷的轻量级Web框架。为啥聊这个?因为作为一名码农,我第一次用Flask时简直惊呆了!!!它让我在几分钟内就搞出一个</div>
                        </li>
                        <li><a href="/article/1938347549920391168.htm"
                               title="算法竞赛备考冲刺必刷题(Python) | 汇总" target="_blank">算法竞赛备考冲刺必刷题(Python) | 汇总</a>
                            <span class="text-muted">热爱编程的通信人</span>
    <a class="tag" taget="_blank" href="/search/%E8%93%9D%E6%A1%A5%E6%9D%AF/1.htm">蓝桥杯</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E7%99%BD%E5%90%8D%E5%8D%95/1.htm">白名单</a>
                            <div>本文分享的必刷题目是从蓝桥云课、洛谷、AcWing等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。欢迎大家订阅我的专栏:算法题解:C++与Python实现!【排序】中等:蓝桥云课301实现基数排序-CSDN博客中等:蓝桥云课1264排个序-CSDN博客中等:蓝桥云</div>
                        </li>
                        <li><a href="/article/1938331283344125952.htm"
                               title="Python web框架FastAPI——一个比Flask和Tornada更高性能的API 框架" target="_blank">Python web框架FastAPI——一个比Flask和Tornada更高性能的API 框架</a>
                            <span class="text-muted">Python进阶者</span>
    <a class="tag" taget="_blank" href="/search/%E4%B8%AD%E9%97%B4%E4%BB%B6/1.htm">中间件</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/web/1.htm">web</a><a class="tag" taget="_blank" href="/search/http/1.htm">http</a><a class="tag" taget="_blank" href="/search/docker/1.htm">docker</a>
                            <div>点击上方“Python爬虫与数据挖掘”,进行关注回复“书籍”即可获赠Python从入门到进阶共10本电子书今日鸡汤借问酒家何处有,牧童遥指杏花村。0前言前几天给大家分别分享了(入门篇)简析Pythonweb框架FastAPI——一个比Flask和Tornada更高性能的API框架和(进阶篇)Pythonweb框架FastAPI——一个比Flask和Tornada更高性能的API框架。今天欢迎大家来</div>
                        </li>
                        <li><a href="/article/1938323211687096320.htm"
                               title="Stable Diffusion提示词终极指南:从手残党到绘画大神的进阶之路(附实战案例)" target="_blank">Stable Diffusion提示词终极指南:从手残党到绘画大神的进阶之路(附实战案例)</a>
                            <span class="text-muted">qq_21422587</span>
    <a class="tag" taget="_blank" href="/search/stable/1.htm">stable</a><a class="tag" taget="_blank" href="/search/diffusion/1.htm">diffusion</a><a class="tag" taget="_blank" href="/search/%E5%85%B6%E4%BB%96/1.htm">其他</a>
                            <div>文章目录一、提示词不是玄学,是门科学(先搞懂底层逻辑)核心三要素(记笔记!):二、新手必看的5大翻车现场(附拯救方案)避坑指南(划重点):三、高能技巧大放送(直接抄作业)四、高阶玩家必备神器(省时省力)五、终极心法(价值百万!)六、常见QA急救站各位老铁们!!!今天咱们要聊的这个话题绝对劲爆——如何用StableDiffusion写出让人跪的提示词?(手把手教学,包教包会)作为把显卡烧坏过三块的老</div>
                        </li>
                        <li><a href="/article/1938323085333688320.htm"
                               title="Selenium自动化测试实战:从入门到精通(附真实案例)" target="_blank">Selenium自动化测试实战:从入门到精通(附真实案例)</a>
                            <span class="text-muted"></span>
    
                            <div>文章目录一、为什么选择Selenium?1.1环境准备三步走二、实战案例:电商网站登录测试2.1基础版脚本2.2进阶技巧三、高级应用:动态数据抓取四、文件上传的终极解决方案4.1改良版代码五、常见问题排雷指南六、最佳实践建议七、总结一、为什么选择Selenium?(敲黑板)在自动化测试领域,Selenium绝对是当之无愧的王者!它支持多种浏览器(Chrome/Firefox/Edge等)、跨平台运</div>
                        </li>
                        <li><a href="/article/1938319301245202432.htm"
                               title="python语言文件A如何调用文件B里的函数" target="_blank">python语言文件A如何调用文件B里的函数</a>
                            <span class="text-muted">SunkingYang</span>
    <a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/python%E5%85%A5%E9%97%A8%E5%BC%80%E5%8F%91/1.htm">python入门开发</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E6%96%87%E4%BB%B6%E8%B0%83%E7%94%A8/1.htm">文件调用</a><a class="tag" taget="_blank" href="/search/%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8/1.htm">函数调用</a><a class="tag" taget="_blank" href="/search/%E8%B0%83%E7%94%A8%E5%85%B6%E4%BB%96%E6%96%87%E4%BB%B6%E5%87%BD%E6%95%B0/1.htm">调用其他文件函数</a>
                            <div>在Python中,文件A调用文件B中的函数主要有以下几种方法,具体实现取决于文件B的路径位置(同一目录或不同目录)。以下是详细操作步骤及代码示例:1.同一目录下的调用若文件A和B位于同一目录,可直接通过import或from...import导入:方法一:导入整个模块#B.py定义函数defadd(x,y):returnx+y#A.py调用importBresult=B.add(1,2)print</div>
                        </li>
                        <li><a href="/article/1938304302338600960.htm"
                               title="CSS进阶-第二十五篇:CSS 与前端工程化-后处理器与模块化(二):CSS 模块化实践" target="_blank">CSS进阶-第二十五篇:CSS 与前端工程化-后处理器与模块化(二):CSS 模块化实践</a>
                            <span class="text-muted">程序员勇哥</span>
    <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E5%85%A8%E5%A5%97%E6%95%99%E7%A8%8B/1.htm">前端全套教程</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a>
                            <div>CSS进阶-第二十五篇:CSS与前端工程化-后处理器与模块化(二):CSS模块化实践在前端工程化的进程中,CSS模块化成为解决样式管理难题的有效方案。它不仅能避免样式污染,还能提升样式的复用性与可维护性。接下来,我们将深入探讨CSSModules的原理、在流行框架中的应用以及样式封装与复用的实践。CSSModules原理实现样式局部作用域CSSModules通过为每个样式类生成唯一的类名,来实现样</div>
                        </li>
                        <li><a href="/article/1938303166172950528.htm"
                               title="前端神器 Handsontable:表格处理的终极解决方案" target="_blank">前端神器 Handsontable:表格处理的终极解决方案</a>
                            <span class="text-muted"></span>
    
                            <div>前端神器Handsontable:表格处理的终极解决方案一、Handsontable简介二、入门使用1.引入Handsontable2.创建基本表格3.常用配置项三、进阶功能与实际效果1.数据编辑与验证四、总结在前端开发领域,表格数据展示与编辑是极为常见的需求。Handsontable作为一款功能强大的JavaScript表格插件,能轻松应对各种复杂的表格场景。本文将对Handsontable展开</div>
                        </li>
                        <li><a href="/article/1938278211548934144.htm"
                               title="《深入解析 C#(第 4 版)》推荐" target="_blank">《深入解析 C#(第 4 版)》推荐</a>
                            <span class="text-muted">阿蒙Armon</span>
    <a class="tag" taget="_blank" href="/search/c%23/1.htm">c#</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                            <div>《深入解析C#(第4版)》推荐在C#语言不断演进的技术浪潮中,《深入解析C#(第4版)》犹如一座灯塔,为开发者照亮探索的道路。无论是经验丰富的老程序员,还是初入C#领域的新手,这本书都能带来满满的收获,堪称C#开发者进阶的必备宝典。从内容架构来看,本书条理清晰、层次分明。全书分为多个部分,从C#背景介绍入手,逐步深入到各个版本的特性讲解。第一部分详细阐述了C#作为一门与时俱进的语言、其背后与时俱进</div>
                        </li>
                        <li><a href="/article/1938245675229704192.htm"
                               title="【MyBatis-Plus保姆级教程】第七章:插件机制与性能优化(SQL分析/防误删/自定义插件)" target="_blank">【MyBatis-Plus保姆级教程】第七章:插件机制与性能优化(SQL分析/防误删/自定义插件)</a>
                            <span class="text-muted"></span>
    
                            <div>摘要:嘿,各位追求卓越的工程师们,我是默语‍!欢迎来到《MyBatis-Plus保姆级教程》的第七章,也是我们深入架构与底层原理的进阶篇!前面的章节我们学会了如何“用”,而这一章我们将探讨如何“用得好、用得安全”。你是否担心过在生产环境误执行了没有WHERE条件的UPDATE或DELETE?你是否想知道哪条SQL执行得最慢,拖累了系统性能?你是否有一些特殊的、全局性的需求(如数据加解密)不知如何优</div>
                        </li>
                                    <li><a href="/article/51.htm"
                                           title="mondb入手" target="_blank">mondb入手</a>
                                        <span class="text-muted">木zi_鸣</span>
    <a class="tag" taget="_blank" href="/search/mongodb/1.htm">mongodb</a>
                                        <div>windows 启动mongodb  编写bat文件, 
     
    mongod --dbpath D:\software\MongoDBDATA 
    mongod --help  查询各种配置 
    配置在mongob 
     
    打开批处理,即可启动,27017原生端口,shell操作监控端口  扩展28017,web端操作端口 
     
    启动配置文件配置, 
     
    数据更灵活 </div>
                                    </li>
                                    <li><a href="/article/178.htm"
                                           title="大型高并发高负载网站的系统架构" target="_blank">大型高并发高负载网站的系统架构</a>
                                        <span class="text-muted">bijian1013</span>
    <a class="tag" taget="_blank" href="/search/%E9%AB%98%E5%B9%B6%E5%8F%91/1.htm">高并发</a><a class="tag" taget="_blank" href="/search/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/1.htm">负载均衡</a>
                                        <div>        扩展Web应用程序 
    一.概念 
            简单的来说,如果一个系统可扩展,那么你可以通过扩展来提供系统的性能。这代表着系统能够容纳更高的负载、更大的数据集,并且系统是可维护的。扩展和语言、某项具体的技术都是无关的。扩展可以分为两种: 
            1.</div>
                                    </li>
                                    <li><a href="/article/305.htm"
                                           title="DISPLAY变量和xhost(原创)" target="_blank">DISPLAY变量和xhost(原创)</a>
                                        <span class="text-muted">czmmiao</span>
    <a class="tag" taget="_blank" href="/search/display/1.htm">display</a>
                                        <div>DISPLAY 
    在Linux/Unix类操作系统上, DISPLAY用来设置将图形显示到何处. 直接登陆图形界面或者登陆命令行界面后使用startx启动图形, DISPLAY环境变量将自动设置为:0:0, 此时可以打开终端, 输出图形程序的名称(比如xclock)来启动程序, 图形将显示在本地窗口上, 在终端上输入printenv查看当前环境变量, 输出结果中有如下内容:DISPLAY=:0.0</div>
                                    </li>
                                    <li><a href="/article/432.htm"
                                           title="获取B/S客户端IP" target="_blank">获取B/S客户端IP</a>
                                        <span class="text-muted">周凡杨</span>
    <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B/1.htm">编程</a><a class="tag" taget="_blank" href="/search/jsp/1.htm">jsp</a><a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a><a class="tag" taget="_blank" href="/search/%E6%B5%8F%E8%A7%88%E5%99%A8/1.htm">浏览器</a>
                                        <div>   最近想写个B/S架构的聊天系统,因为以前做过C/S架构的QQ聊天系统,所以对于Socket通信编程只是一个巩固。对于C/S架构的聊天系统,由于存在客户端Java应用,所以直接在代码中获取客户端的IP,应用的方法为: 
       String ip = InetAddress.getLocalHost().getHostAddress(); 
    然而对于WEB</div>
                                    </li>
                                    <li><a href="/article/559.htm"
                                           title="浅谈类和对象" target="_blank">浅谈类和对象</a>
                                        <span class="text-muted">朱辉辉33</span>
    <a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B/1.htm">编程</a>
                                        <div>    类是对一类事物的总称,对象是描述一个物体的特征,类是对象的抽象。简单来说,类是抽象的,不占用内存,对象是具体的, 
    占用存储空间。 
        类是由属性和方法构成的,基本格式是public  class 类名{ 
     
     //定义属性 
     private/public 数据类型 属性名; 
     
     //定义方法 
     publ</div>
                                    </li>
                                    <li><a href="/article/686.htm"
                                           title="android activity与viewpager+fragment的生命周期问题" target="_blank">android activity与viewpager+fragment的生命周期问题</a>
                                        <span class="text-muted">肆无忌惮_</span>
    <a class="tag" taget="_blank" href="/search/viewpager/1.htm">viewpager</a>
                                        <div>有一个Activity里面是ViewPager,ViewPager里面放了两个Fragment。 
    第一次进入这个Activity。开启了服务,并在onResume方法中绑定服务后,对Service进行了一定的初始化,其中调用了Fragment中的一个属性。 
    		super.onResume();
    		bindService(intent, conn, BIND_AUTO_CREATE);
    </div>
                                    </li>
                                    <li><a href="/article/813.htm"
                                           title="base64Encode对图片进行编码" target="_blank">base64Encode对图片进行编码</a>
                                        <span class="text-muted">843977358</span>
    <a class="tag" taget="_blank" href="/search/base64/1.htm">base64</a><a class="tag" taget="_blank" href="/search/%E5%9B%BE%E7%89%87/1.htm">图片</a><a class="tag" taget="_blank" href="/search/encoder/1.htm">encoder</a>
                                        <div>/**
    	 * 对图片进行base64encoder编码
    	 * 
    	 * @author mrZhang
    	 * @param path
    	 * @return
    	 */
    	public static String encodeImage(String path) {
    		BASE64Encoder encoder = null;
    		byte[] b = null;
    		I</div>
                                    </li>
                                    <li><a href="/article/940.htm"
                                           title="Request Header简介" target="_blank">Request Header简介</a>
                                        <span class="text-muted">aigo</span>
    <a class="tag" taget="_blank" href="/search/servlet/1.htm">servlet</a>
                                        <div>当一个客户端(通常是浏览器)向Web服务器发送一个请求是,它要发送一个请求的命令行,一般是GET或POST命令,当发送POST命令时,它还必须向服务器发送一个叫“Content-Length”的请求头(Request   Header)   用以指明请求数据的长度,除了Content-Length之外,它还可以向服务器发送其它一些Headers,如:    </div>
                                    </li>
                                    <li><a href="/article/1067.htm"
                                           title="HttpClient4.3 创建SSL协议的HttpClient对象" target="_blank">HttpClient4.3 创建SSL协议的HttpClient对象</a>
                                        <span class="text-muted">alleni123</span>
    <a class="tag" taget="_blank" href="/search/httpclient/1.htm">httpclient</a><a class="tag" taget="_blank" href="/search/%E7%88%AC%E8%99%AB/1.htm">爬虫</a><a class="tag" taget="_blank" href="/search/ssl/1.htm">ssl</a>
                                        <div>public class HttpClientUtils
    {
    	public static CloseableHttpClient createSSLClientDefault(CookieStore cookies){
    		SSLContext sslContext=null;
    		
    		try
    		{
    			sslContext=new SSLContextBuilder().l</div>
                                    </li>
                                    <li><a href="/article/1194.htm"
                                           title="java取反 -右移-左移-无符号右移的探讨" target="_blank">java取反 -右移-左移-无符号右移的探讨</a>
                                        <span class="text-muted">百合不是茶</span>
    <a class="tag" taget="_blank" href="/search/%E4%BD%8D%E8%BF%90%E7%AE%97%E7%AC%A6+%E4%BD%8D%E7%A7%BB/1.htm">位运算符 位移</a>
                                        <div>取反:
    		在二进制中第一位,1表示符数,0表示正数
    		byte a = -1;
    		原码:10000001
    		反码:11111110
    		补码:11111111
          //异或: 00000000
    		byte b = -2;
    		原码:10000010
    		反码:11111101
    		补码:11111110
          //异或: 00000001
    		
    	</div>
                                    </li>
                                    <li><a href="/article/1321.htm"
                                           title="java多线程join的作用与用法" target="_blank">java多线程join的作用与用法</a>
                                        <span class="text-muted">bijian1013</span>
    <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%A4%9A%E7%BA%BF%E7%A8%8B/1.htm">多线程</a>
                                        <div>        对于JAVA的join,JDK 是这样说的:join public final void join (long millis )throws InterruptedException Waits at most millis milliseconds for this thread to die. A timeout of 0 means t</div>
                                    </li>
                                    <li><a href="/article/1448.htm"
                                           title="Java发送http请求(get 与post方法请求)" target="_blank">Java发送http请求(get 与post方法请求)</a>
                                        <span class="text-muted">bijian1013</span>
    <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a>
                                        <div>PostRequest.java 
    package com.bijian.study;
    
    import java.io.BufferedReader;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.HttpURL</div>
                                    </li>
                                    <li><a href="/article/1575.htm"
                                           title="【Struts2二】struts.xml中package下的action配置项默认值" target="_blank">【Struts2二】struts.xml中package下的action配置项默认值</a>
                                        <span class="text-muted">bit1129</span>
    <a class="tag" taget="_blank" href="/search/struts.xml/1.htm">struts.xml</a>
                                        <div>在第一部份,定义了struts.xml文件,如下所示: 
      
    <!DOCTYPE struts PUBLIC
            "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
            "http://struts.apache.org/dtds/struts</div>
                                    </li>
                                    <li><a href="/article/1702.htm"
                                           title="【Kafka十三】Kafka Simple Consumer" target="_blank">【Kafka十三】Kafka Simple Consumer</a>
                                        <span class="text-muted">bit1129</span>
    <a class="tag" taget="_blank" href="/search/simple/1.htm">simple</a>
                                        <div>代码中关于Host和Port是割裂开的,这会导致单机环境下的伪分布式Kafka集群环境下,这个例子没法运行。 
    实际情况是需要将host和port绑定到一起, 
      
    package kafka.examples.lowlevel;
    
    import kafka.api.FetchRequest;
    import kafka.api.FetchRequestBuilder;
    impo</div>
                                    </li>
                                    <li><a href="/article/1829.htm"
                                           title="nodejs学习api" target="_blank">nodejs学习api</a>
                                        <span class="text-muted">ronin47</span>
    <a class="tag" taget="_blank" href="/search/nodejs+api/1.htm">nodejs api</a>
                                        <div>NodeJS基础 什么是NodeJS 
    JS是脚本语言,脚本语言都需要一个解析器才能运行。对于写在HTML页面里的JS,浏览器充当了解析器的角色。而对于需要独立运行的JS,NodeJS就是一个解析器。 
    每一种解析器都是一个运行环境,不但允许JS定义各种数据结构,进行各种计算,还允许JS使用运行环境提供的内置对象和方法做一些事情。例如运行在浏览器中的JS的用途是操作DOM,浏览器就提供了docum</div>
                                    </li>
                                    <li><a href="/article/1956.htm"
                                           title="java-64.寻找第N个丑数" target="_blank">java-64.寻找第N个丑数</a>
                                        <span class="text-muted">bylijinnan</span>
    <a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                        <div>
    
    
    public class UglyNumber {
    
    	/**
    	 * 64.查找第N个丑数
    具体思路可参考 [url] http://zhedahht.blog.163.com/blog/static/2541117420094245366965/[/url]
    	 * 
    题目:我们把只包含因子
    2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14</div>
                                    </li>
                                    <li><a href="/article/2083.htm"
                                           title="二维数组(矩阵)对角线输出" target="_blank">二维数组(矩阵)对角线输出</a>
                                        <span class="text-muted">bylijinnan</span>
    <a class="tag" taget="_blank" href="/search/%E4%BA%8C%E7%BB%B4%E6%95%B0%E7%BB%84/1.htm">二维数组</a>
                                        <div>
    
    /**
    二维数组 对角线输出 两个方向
    例如对于数组:
    { 1, 2, 3, 4 }, 
    { 5, 6, 7, 8 },
    { 9, 10, 11, 12 }, 
    { 13, 14, 15, 16 },
    
    slash方向输出:
    1 
    5 2 
    9 6 3 
    13 10 7 4 
    14 11 8 
    15 12 
    16 
    
    backslash输出:
    4 
    3</div>
                                    </li>
                                    <li><a href="/article/2210.htm"
                                           title="[JWFD开源工作流设计]工作流跳跃模式开发关键点(今日更新)" target="_blank">[JWFD开源工作流设计]工作流跳跃模式开发关键点(今日更新)</a>
                                        <span class="text-muted">comsci</span>
    <a class="tag" taget="_blank" href="/search/%E5%B7%A5%E4%BD%9C%E6%B5%81/1.htm">工作流</a>
                                        <div>   既然是做开源软件的,我们的宗旨就是给大家分享设计和代码,那么现在我就用很简单扼要的语言来透露这个跳跃模式的设计原理 
     
       大家如果用过JWFD的ARC-自动运行控制器,或者看过代码,应该知道在ARC算法模块中有一个函数叫做SAN(),这个函数就是ARC的核心控制器,要实现跳跃模式,在SAN函数中一定要对LN链表数据结构进行操作,首先写一段代码,把</div>
                                    </li>
                                    <li><a href="/article/2337.htm"
                                           title="redis常见使用" target="_blank">redis常见使用</a>
                                        <span class="text-muted">cuityang</span>
    <a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a><a class="tag" taget="_blank" href="/search/%E5%B8%B8%E8%A7%81%E4%BD%BF%E7%94%A8/1.htm">常见使用</a>
                                        <div>redis 通常被认为是一个数据结构服务器,主要是因为其有着丰富的数据结构 strings、map、 list、sets、 sorted sets 
     
    引入jar包 jedis-2.1.0.jar  (本文下方提供下载) 
     
    package redistest; 
     
    import redis.clients.jedis.Jedis; 
     
    public class Listtest</div>
                                    </li>
                                    <li><a href="/article/2464.htm"
                                           title="配置多个redis" target="_blank">配置多个redis</a>
                                        <span class="text-muted">dalan_123</span>
    <a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a>
                                        <div>配置多个redis客户端 
    <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi=&quo</div>
                                    </li>
                                    <li><a href="/article/2591.htm"
                                           title="attrib命令" target="_blank">attrib命令</a>
                                        <span class="text-muted">dcj3sjt126com</span>
    <a class="tag" taget="_blank" href="/search/attr/1.htm">attr</a>
                                        <div>     
    attrib指令用于修改文件的属性.文件的常见属性有:只读.存档.隐藏和系统.      
      只读属性是指文件只可以做读的操作.不能对文件进行写的操作.就是文件的写保护.  
     
      存档属性是用来标记文件改动的.即在上一次备份后文件有所改动.一些备份软件在备份的时候会只去备份带有存档属性的文件.  
     </div>
                                    </li>
                                    <li><a href="/article/2718.htm"
                                           title="Yii使用公共函数" target="_blank">Yii使用公共函数</a>
                                        <span class="text-muted">dcj3sjt126com</span>
    <a class="tag" taget="_blank" href="/search/yii/1.htm">yii</a>
                                        <div>在网站项目中,没必要把公用的函数写成一个工具类,有时候面向过程其实更方便。   在入口文件index.php里添加   require_once('protected/function.php');   即可对其引用,成为公用的函数集合。   function.php如下:   
       <?php   /**     * This is the shortcut to D</div>
                                    </li>
                                    <li><a href="/article/2845.htm"
                                           title="linux 系统资源的查看(free、uname、uptime、netstat)" target="_blank">linux 系统资源的查看(free、uname、uptime、netstat)</a>
                                        <span class="text-muted">eksliang</span>
    <a class="tag" taget="_blank" href="/search/netstat/1.htm">netstat</a><a class="tag" taget="_blank" href="/search/linux+uname/1.htm">linux uname</a><a class="tag" taget="_blank" href="/search/linux+uptime/1.htm">linux uptime</a><a class="tag" taget="_blank" href="/search/linux+free/1.htm">linux free</a>
                                        <div>linux 系统资源的查看 
    转载请出自出处:http://eksliang.iteye.com/blog/2167081 
      
    http://eksliang.iteye.com 一、free查看内存的使用情况 
    语法如下: 
      
    free [-b][-k][-m][-g] [-t]
    参数含义
    -b:直接输入free时,显示的单位是kb我们可以使用b(bytes),m</div>
                                    </li>
                                    <li><a href="/article/2972.htm"
                                           title="JAVA的位操作符" target="_blank">JAVA的位操作符</a>
                                        <span class="text-muted">greemranqq</span>
    <a class="tag" taget="_blank" href="/search/%E4%BD%8D%E8%BF%90%E7%AE%97/1.htm">位运算</a><a class="tag" taget="_blank" href="/search/JAVA%E4%BD%8D%E7%A7%BB/1.htm">JAVA位移</a><a class="tag" taget="_blank" href="/search/%3C%3C/1.htm"><<</a><a class="tag" taget="_blank" href="/search/%3E%3E%3E/1.htm">>>></a>
                                        <div>最近几种进制,加上各种位操作符,发现都比较模糊,不能完全掌握,这里就再熟悉熟悉。 
      
    1.按位操作符 : 
       按位操作符是用来操作基本数据类型中的单个bit,即二进制位,会对两个参数执行布尔代数运算,获得结果。 
       与(&)运算: 
       1&1 = 1, 1&0 = 0, 0&0 &</div>
                                    </li>
                                    <li><a href="/article/3099.htm"
                                           title="Web前段学习网站" target="_blank">Web前段学习网站</a>
                                        <span class="text-muted">ihuning</span>
    <a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a>
                                        <div>  
    Web前段学习网站 
    菜鸟学习:http://www.w3cschool.cc/ 
      
    JQuery中文网:http://www.jquerycn.cn/ 
      
    内存溢出:http://outofmemory.cn/#csdn.blog 
      
    http://www.icoolxue.com/ 
      
    http://www.jikexue</div>
                                    </li>
                                    <li><a href="/article/3226.htm"
                                           title="强强联合:FluxBB 作者加盟 Flarum" target="_blank">强强联合:FluxBB 作者加盟 Flarum</a>
                                        <span class="text-muted">justjavac</span>
    <a class="tag" taget="_blank" href="/search/r/1.htm">r</a>
                                        <div>原文:FluxBB Joins Forces With Flarum作者:Toby Zerner译文:强强联合:FluxBB 作者加盟 Flarum译者:justjavac  
    FluxBB 是一个快速、轻量级论坛软件,它的开发者是一名德国的 PHP 天才 Franz Liedke。FluxBB 的下一个版本(2.0)将被完全重写,并已经开发了一段时间。FluxBB 看起来非常有前途的,</div>
                                    </li>
                                    <li><a href="/article/3353.htm"
                                           title="java统计在线人数(session存储信息的)" target="_blank">java统计在线人数(session存储信息的)</a>
                                        <span class="text-muted">macroli</span>
    <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a>
                                        <div>这篇日志是我写的第三次了 前两次都发布失败!郁闷极了! 
      
    由于在web开发中常常用到这一部分所以在此记录一下,呵呵,就到备忘录了! 
    我对于登录信息时使用session存储的,所以我这里是通过实现HttpSessionAttributeListener这个接口完成的。 
    1、实现接口类,在web.xml文件中配置监听类,从而可以使该类完成其工作。 
    public class Ses</div>
                                    </li>
                                    <li><a href="/article/3480.htm"
                                           title="bootstrp carousel初体验 快速构建图片播放" target="_blank">bootstrp carousel初体验 快速构建图片播放</a>
                                        <span class="text-muted">qiaolevip</span>
    <a class="tag" taget="_blank" href="/search/%E6%AF%8F%E5%A4%A9%E8%BF%9B%E6%AD%A5%E4%B8%80%E7%82%B9%E7%82%B9/1.htm">每天进步一点点</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E6%B0%B8%E6%97%A0%E6%AD%A2%E5%A2%83/1.htm">学习永无止境</a><a class="tag" taget="_blank" href="/search/bootstrap/1.htm">bootstrap</a><a class="tag" taget="_blank" href="/search/%E7%BA%B5%E8%A7%82%E5%8D%83%E8%B1%A1/1.htm">纵观千象</a>
                                        <div>img{
    			border: 1px solid white;
    			box-shadow: 2px 2px 12px #333;
    			_width: expression(this.width > 600 ? "600px" : this.width + "px");
    			_height: expression(this.width &</div>
                                    </li>
                                    <li><a href="/article/3607.htm"
                                           title="SparkSQL读取HBase数据,通过自定义外部数据源" target="_blank">SparkSQL读取HBase数据,通过自定义外部数据源</a>
                                        <span class="text-muted">superlxw1234</span>
    <a class="tag" taget="_blank" href="/search/spark/1.htm">spark</a><a class="tag" taget="_blank" href="/search/sparksql/1.htm">sparksql</a><a class="tag" taget="_blank" href="/search/sparksql%E8%AF%BB%E5%8F%96hbase/1.htm">sparksql读取hbase</a><a class="tag" taget="_blank" href="/search/sparksql%E5%A4%96%E9%83%A8%E6%95%B0%E6%8D%AE%E6%BA%90/1.htm">sparksql外部数据源</a>
                                        <div>关键字:SparkSQL读取HBase、SparkSQL自定义外部数据源 
      
      
    前面文章介绍了SparSQL通过Hive操作HBase表。 
      
    SparkSQL从1.2开始支持自定义外部数据源(External DataSource),这样就可以通过API接口来实现自己的外部数据源。这里基于Spark1.4.0,简单介绍SparkSQL自定义外部数据源,访</div>
                                    </li>
                                    <li><a href="/article/3734.htm"
                                           title="Spring Boot 1.3.0.M1发布" target="_blank">Spring Boot 1.3.0.M1发布</a>
                                        <span class="text-muted">wiselyman</span>
    <a class="tag" taget="_blank" href="/search/spring+boot/1.htm">spring boot</a>
                                        <div>    Spring Boot 1.3.0.M1于6.12日发布,现在可以从Spring milestone repository下载。这个版本是基于Spring Framework 4.2.0.RC1,并在Spring Boot 1.2之上提供了大量的新特性improvements and new features。主要包含以下: 
      
    1.提供一个新的sprin</div>
                                    </li>
                    </ul>
                </div>
            </div>
        </div>
    
    <div>
        <div class="container">
            <div class="indexes">
                <strong>按字母分类:</strong>
                <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a
                    href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a
                    href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a
                    href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a
                    href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a
                    href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a
                    href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a
                    href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a
                    href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a>
            </div>
        </div>
    </div>
    <footer id="footer" class="mb30 mt30">
        <div class="container">
            <div class="footBglm">
                <a target="_blank" href="/">首页</a> -
                <a target="_blank" href="/custom/about.htm">关于我们</a> -
                <a target="_blank" href="/search/Java/1.htm">站内搜索</a> -
                <a target="_blank" href="/sitemap.txt">Sitemap</a> -
                <a target="_blank" href="/custom/delete.htm">侵权投诉</a>
            </div>
            <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved.
    <!--            <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>-->
            </div>
        </div>
    </footer>
    <!-- 代码高亮 -->
    <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script>
    <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script>
    <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script>
    <link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/>
    <script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script>
    
    
    
    
    
    </body>
    
    </html>